轻松的DataFrame清理技术,范围从删除有问题的行到选择重要的列。




pyspark 的LDA的参数设置 pyspark filter_pyspark 的LDA的参数设置


成为一名数据工程师会使事情变得无法清晰表达。 似乎不可避免的是,每个善意的Spark教程都注定会在没有太多解释的情况下陷入难以理解的代码墙。 这在StackOverflow中甚至显而易见,在这里,简单的问题经常会被荒谬的不必要的解决方案所满足(停止为所有内容制作UDF!)无论如何,我要说的是,点击这些内容需要很多勇气,在这里您 是。 我感激你。

在上一集中,我们介绍了一些Spark基础知识,与Databricks一起使用,并开始将数据加载到DataFrames中。 现在,我们通过接触每个数据爱好者最喜欢的主题:清理数据,来更深入地研究DataFrames。 为了使它有趣,我选择了我能找到的最肮脏的数据集:FDA药物执行!

排行

我们不必长时间查看这些数据就可以发现一些漏洞:


pyspark 的LDA的参数设置 pyspark filter_字符串_02


查看所有这些空单元格。 耻辱。 让我们来应对这些麻烦制造者。

删除具有空值的行

如果您是Pandas的粉丝,您可能会想"这是.dropna()的工作!"事实证明,您的定位可能比您想像的要多-PySpark DataFrames也提供了一种删除NA的方法 / A值,它恰好被称为.dropna()!

df = df.dropna()display(df)

关键字参数将使您有宾至如归的感觉:

· 方法:接受"任何"或"全部"。 如果为" any",则包含所有空值的行将被完全删除(默认)。 如果为"全部",则仅删除完全为空的行。

· thresh:接受表示"阈值"的整数,表示行必须删除之前必须有多少个空单元格。 Tresh是how = any和how = all之间的中间地带。 结果,脱粒的存在将覆盖如何脱粒。

· 子集:接受列名称列表。 存在子集时,将仅根据提供名称的列检查N / A值。

PySpark没有就位的概念,因此,仅当我们将DataFrame设置为等于受影响DataFrame的值(df = df.dropna())时,才会应用针对DataFrame运行的任何方法。

我的数据集太脏了,以至于运行dropna()实际上删除了所有500行! 是的,实际上每行中都有一个空单元格。 这是我们将从列名传递到子集的好处:

df = df.dropna(subset=['postal_code', 'city', 'country', 'address_1'])display(df)

事情看起来已经很干净了:


pyspark 的LDA的参数设置 pyspark filter_pyspark udf 多个参数_03


替换N / A值

尽管N / A值可能会损害我们的分析,但有时完全放弃这些行甚至会带来更多问题。 考虑一下我们想获得汇总数据的情况:通过从总池中删除记录并删除本应计数的记录,删除整行将很容易歪曲汇总统计信息。 在这些情况下,fillna()可以为您提供帮助。

fillna()接受一个值,并将用该值替换找到的所有空单元格,而不是删除行:

df = df.fillna(0)display(df)

fillna()还接受可选的子集参数,就像dropna()一样。

删除重复的行

清理数据的前十大方法是dropduplicates()方法。 就其本身而言,在DataFrame上调用dropduplicates()会删除行,其中一行中的所有值都被另一行重复。 与到目前为止我们介绍的其他两种方法一样,dropduplicates()也接受subset参数:

df = df.dropduplicates(subset="recall_number")display(df)

从数据框选择数据

还有一个问题:它有很多无用的列。 不,认真地,检查一下运行df.printSchema()时会发生什么:

root |-- classification: string (nullable = true) |-- center_classification_date: timestamp (nullable = true) |-- report_date: timestamp (nullable = true) |-- postal_code: string (nullable = true) |-- termination_date: timestamp (nullable = true) |-- recall_initiation_date: timestamp (nullable = true) |-- recall_number: string (nullable = true) |-- city: string (nullable = true) |-- more_code_info: string (nullable = true) |-- event_id: integer (nullable = true) |-- distribution_pattern: string (nullable = true) |-- openfda_application_number: string (nullable = true) |-- openfda_brand_name: string (nullable = true) |-- openfda_dosage_form: string (nullable = true) |-- openfda_generic_name: string (nullable = true) |-- openfda_manufacturer_name: string (nullable = true) |-- openfda_product_ndc: string (nullable = true) |-- openfda_product_type: string (nullable = true) |-- openfda_route: string (nullable = true) |-- openfda_substance_name: string (nullable = true) |-- openfda_spl_id: string (nullable = true) |-- openfda_spl_set_id: string (nullable = true) |-- openfda_pharm_class_moa: string (nullable = true) |-- openfda_pharm_class_cs: string (nullable = true) |-- openfda_pharm_class_pe: string (nullable = true) |-- openfda_pharm_class_epc: string (nullable = true) |-- openfda_upc: string (nullable = true) |-- openfda_unii: string (nullable = true) |-- openfda_rxcui: string (nullable = true) |-- recalling_firm: string (nullable = true) |-- voluntary_mandated: string (nullable = true) |-- state: string (nullable = true) |-- reason_for_recall: string (nullable = true) |-- initial_firm_notification: string (nullable = true) |-- status: string (nullable = true) |-- product_type: string (nullable = true) |-- country: string (nullable = true) |-- product_description: string (nullable = true) |-- code_info: string (nullable = true) |-- address_1: string (nullable = true) |-- address_2: string (nullable = true) |-- product_quantity: string (nullable = true)

我什至不知道其中的一些专栏。 即使我删除了我不认识的每一列,我仍然会浪费大量时间在此列表上。 幸运的是,我们有select()。 当我们在DataFrame上调用select()时,我们可以显式地调出要保留的列:

df = df.select('classification', 'report_date', 'termination_date', 'city', 'distribution_pattern', 'status', 'product_type', 'country', 'product_description', 'address_1', 'address_2', 'product_quantity')display(df)

我们的数据开始看起来可以被人类消化(不同于表中所列的可能可怕的药物):


pyspark 的LDA的参数设置 pyspark filter_ci_04


筛选资料

有时,我们将处理包含许多异常值的数据,这些异常值会扭曲我们的结果。 有时候,我们只希望处理属于特定信息子集的行。 我们将按照以下方式清理数据,使其仅包含发生在南旧金山的案件:

df = df.filter(df.city == "South San Francisco")

filter()的内容将始终是一个条件,在此条件下,我们会将特定列中的值与预期值进行比较。 访问DataFrame列的最简单方法是使用df.column_name语法。

在本例中,我们正在将包含字符串的列与提供的字符串South San Francisco进行比较(对于数值,我们也可以使用大于和小于运算符)。

按字符串值过滤

除了通过完美匹配进行过滤之外,还有许多其他强大的方法可以在PySpark中通过字符串进行过滤。 看一看:

· df.filter(df.city.contains('San Francisco'):返回其中列的字符串包含提供的子字符串的行,在我们的示例中,按包含子字符串" San Francisco"的行进行过滤将是一种很好的方法 旧金山的所有行,而不仅仅是"南旧金山"。

· df.filter(df.city.startswith('San')):返回其中字符串以提供的子字符串开头的行。

· df.filter(df.city.endswith('ice')):返回其中字符串以提供的子字符串开头的行。

· df.filter(df.city.isNull()):返回其中提供的列中的值为空的行。

· df.filter(df.city.isNotNull()):与上述相反。

· df.filter(df.city.like('San%')):执行包含LIKE子句的类似SQL的查询。

· df.filter(df.city.rlike('[A-Z] * ice $')):执行一个正则表达式过滤器。

· df.filter(df.city.isin('San Francisco','Los Angeles')):查找行,其中列的字符串值与提供的任何字符串完全匹配。

除了按字符串过滤外,我们还可以按将值存储为日期或日期时间的列进行过滤。 也许最有用的日期过滤方法是使用between()方法,它使我们能够在特定日期范围内查找结果。 在这里,我们找到了2013年和2014年报告的所有结果:

df = df.filter(df.report_date.between('2013-01-01 00:00:00','2015-01-11 00:00:00'))

通过where()过滤

.where()是达到与filter()相同效果的另一种方式:

df = df.where((df.city == "South San Francisco"))

排序我们的数据框

最后,有几种方法可以根据喜好对DataFrame中的数据进行排序。 我的首选方法是使用orderBy():

df = df.orderBy('report_date', ascending=False)

orderBy()以我们期望的方式对结果进行排序:字符串列按字母顺序排序,数字列按时间顺序排序,依此类推。ascending关键字参数允许我们在升序等于False时显示这些结果的降序。

请注意,我们是如何将" report_date"作为字符串传递的,而不是df.report_date? PySpark出于某些原因允许我们执行此操作。 但是,这在过滤时将不起作用,因为df = df.filter(" city" ==" South San Francisco")似乎我们正在尝试根据字符串评估字符串。

获得相同效果的更糟糕的方法是使用sort()方法。 sort()比orderBy()更糟糕,因为语法更丑陋,并且因为它要求我们导入一些东西才以降序列出结果:

from pyspark.sql.functions import descdf = df.sort(desc("published_at"))

那就是我们今天所有的时间。 下次当我们探索在PySpark中转换DataFrames的神奇世界时,请加入我们。