我有一个pyspark数据框,如下所示。
+---+-------+--------+
|age|balance|duration|
+---+-------+--------+
| 2| 2143| 261|
| 44| 29| 151|
| 33| 2| 76|
| 50| 1506| 92|
| 33| 1| 198|
| 35| 231| 139|
| 28| 447| 217|
| 2| 2| 380|
| 58| 121| 50|
| 43| 693| 55|
| 41| 270| 222|
| 50| 390| 137|
| 53| 6| 517|
| 58| 71| 71|
| 57| 162| 174|
| 40| 229| 353|
| 45| 13| 98|
| 57| 52| 38|
| 3| 0| 219|
| 4| 0| 54|
+---+-------+--------+
我的预期输出应该像
+---+-------+--------+-------+-----------+------------+
|age|balance|duration|age_out|balance_out|duration_out|
+---+-------+--------+-------+-----------+------------+
| 2| 2143| 261| 1| 1| 0|
| 44| 29| 151| 0| 0| 0|
| 33| 2| 76| 0| 0| 0|
| 50| 1506| 92| 0| 1| 0|
| 33| 1| 198| 0| 0| 0|
| 35| 231| 139| 0| 0| 0|
| 28| 447| 217| 0| 0| 0|
| 2| 2| 380| 1| 0| 0|
| 58| 121| 50| 0| 0| 0|
| 43| 693| 55| 0| 0| 0|
| 41| 270| 222| 0| 0| 0|
| 50| 390| 137| 0| 0| 0|
| 53| 6| 517| 0| 0| 1|
| 58| 71| 71| 0| 0| 0|
| 57| 162| 174| 0| 0| 0|
| 40| 229| 353| 0| 0| 0|
| 45| 13| 98| 0| 0| 0|
| 57| 52| 38| 0| 0| 0|
| 3| 0| 219| 1| 0| 0|
| 4| 0| 54| 0| 0| 0|
+---+-------+--------+-------+-----------+------------+
这里,我的目标是使用四分位间距方法(如下面的python代码中所述)来识别数据集中的异常记录。如果发现任何异常记录,则需要将它们标记为1,否则标记为0。
通过使用以下代码,我可以使用python做同样的事情。
import numpy as np
def outliers_iqr(ys):
quartile_1, quartile_3 = np.percentile(ys, [25, 75])
iqr = quartile_3 - quartile_1
lower_bound = quartile_1 - (iqr * 1.5)
upper_bound = quartile_3 + (iqr * 1.5)
ser = np.zeros(len(ys))
pos =np.where((ys > upper_bound) | (ys < lower_bound))[0]
ser[pos]=1
return(ser)
但是我想在pyspark中做同样的事情。有人可以帮我吗?
我的pyspark代码:
def outliers_iqr(ys):
quartile_1, quartile_3 = np.percentile(ys, [25, 75])
iqr = quartile_3 - quartile_1
lower_bound = quartile_1 - (iqr * 1.5)
upper_bound = quartile_3 + (iqr * 1.5)
ser = np.zeros(len(ys))
pos =np.where((ys > upper_bound) | (ys < lower_bound))[0]
ser[pos]=1
return(float(ser))
outliers_iqr_udf = udf(outliers_iqr, FloatType())
DF.withColumn('age_out', outliers_iqr_udf(DF.select('age').collect())).show()
答案 0 :(得分:2)
You can use pyspark.sql.DataFrame.approxQuantile
以获得每个列的所需第25和第75个百分位值。
bounds = {
c: dict(
zip(["q1", "q3"], df.approxQuantile(c, [0.25, 0.75], 0))
)
for c in df.columns
}
最后一个传递的参数是相对错误,您可以在链接的文章以及docs上阅读有关的错误。简短的说法是,数字越小,您的结果将越准确,但是要在准确性和计算费用之间进行权衡。 (这里我使用0来获取确切的值,但是您可能要根据数据的大小选择其他值。)
一旦有了第一个和第三个四分位数的值,就可以很容易地计算出iqr
和上限/下限:
for c in bounds:
iqr = bounds[c]['q3'] - bounds[c]['q1']
bounds[c]['lower'] = bounds[c]['q1'] - (iqr * 1.5)
bounds[c]['upper'] = bounds[c]['q3'] + (iqr * 1.5)
print(bounds)
#{'age': {'lower': 3.0, 'q1': 33.0, 'q3': 53.0, 'upper': 83.0},
# 'balance': {'lower': -570.0, 'q1': 6.0, 'q3': 390.0, 'upper': 966.0},
# 'duration': {'lower': -143.0, 'q1': 76.0, 'q3': 222.0, 'upper': 441.0}}
现在在列表理解中使用pyspark.sql.functions.when
基于bounds
构建异常列:
import pyspark.sql.functions as f
df.select(
"*",
*[
f.when(
f.col(c).between(bounds[c]['lower'], bounds[c]['upper']),
0
).otherwise(1).alias(c+"_out")
for c in df.columns
]
).show()
#+---+-------+--------+-------+-----------+------------+
#|age|balance|duration|age_out|balance_out|duration_out|
#+---+-------+--------+-------+-----------+------------+
#| 2| 2143| 261| 1| 1| 0|
#| 44| 29| 151| 0| 0| 0|
#| 33| 2| 76| 0| 0| 0|
#| 50| 1506| 92| 0| 1| 0|
#| 33| 1| 198| 0| 0| 0|
#| 35| 231| 139| 0| 0| 0|
#| 28| 447| 217| 0| 0| 0|
#| 2| 2| 380| 1| 0| 0|
#| 58| 121| 50| 0| 0| 0|
#| 43| 693| 55| 0| 0| 0|
#| 41| 270| 222| 0| 0| 0|
#| 50| 390| 137| 0| 0| 0|
#| 53| 6| 517| 0| 0| 1|
#| 58| 71| 71| 0| 0| 0|
#| 57| 162| 174| 0| 0| 0|
#| 40| 229| 353| 0| 0| 0|
#| 45| 13| 98| 0| 0| 0|
#| 57| 52| 38| 0| 0| 0|
#| 3| 0| 219| 0| 0| 0|
#| 4| 0| 54| 0| 0| 0|
#+---+-------+--------+-------+-----------+------------+
在这里,我使用between
来检查值是否不是异常值,并且此函数具有包容性(即,x between a and b
在逻辑上等效于x >= a and x <= b
)。
答案 1 :(得分:0)
请在我的解决方案下面找到
from pyspark.sql import functions as f
class Outlier():
def __init__(self, df):
self.df = df
def _calculate_bounds(self):
bounds = {
c: dict(
zip(["q1", "q3"], self.df.approxQuantile(c, [0.25, 0.75], 0))
)
for c, d in zip(self.df.columns, self.df.dtypes) if d[1] in ["bigint", "double"]
}
for c in bounds:
iqr = bounds[c]['q3'] - bounds[c]['q1']
bounds[c]['min'] = bounds[c]['q1'] - (iqr * 1.5)
bounds[c]['max'] = bounds[c]['q3'] + (iqr * 1.5)
return bounds
def _flag_outliers_df(self):
bounds = self._calculate_bounds()
outliers_col = [
f.when(
~f.col(c).between(bounds[c]['min'], bounds[c]['max']),
f.col(c)
).alias(c + '_outlier')
for c in bounds]
return self.df.select(*outliers_col)
def show_outliers(self):
outlier_df = self._flag_outliers_df()
for outlier in outlier_df.columns:
outlier_df.select(outlier).filter(f.col(outlier).isNotNull()).show()
然后按如下所示传递数据框:
Outlier(df).show_outliers()