Pyspark从列中获取最新的更新值

时间:2018-11-09 16:43:49

标签: apache-spark dataframe pyspark

我有一个如下数据框:

+----+--------+--------+------+
| id | value1 | value2 | flag |
+----+--------+--------+------+
|  1 | 7000   | 30     |   0  |
|  2 | 0      | 9      |   0  |
|  3 | 23627  | 17     |   1  |
|  4 | 8373   | 23     |   0  |
|  5 | -0.5   | 4      |   1  |
+----+--------+--------+------+

我要运行以下条件-
1.如果value大于0,我希望前一行为value2
2.如果value等于0,我想要上一行和下一行的平均值2
3.如果值小于0,则为NULL
  所以我写了下面的代码-

df = df.withColumn('value2',when(col(value1)>0,lag(col(value2))).when(col(value1)==0,\
                   (lag(col(value2))+lead(col(value2)))/2.0).otherwise(None))

我想要的是在获取上一行和下一行的值时应具有更新后的值,如下所示。应该按照找到它们的顺序进行操作,首先对id-1进行更新,然后对id-2进行更新,以此类推。

+----+--------+--------+------+
| id | value1 | value2 | flag |
+----+--------+--------+------+
|  1 | 7000   | null   |   0  |
|  2 | 0      | 8.5    |   0  |
|  3 | 23627  | 8.5    |   1  |
|  4 | 8373   | 8.5    |   0  |
|  5 | -0.5   | null   |   1  |
+----+--------+--------+------+

我尝试通过在何时重新分配数据帧时给id == 1来进行操作,然后在操作时再次使用列进行操作。

df = df.withColumn('value2',when((col(id)==1)&(col(value1)>0,lag(col(value2)))
\.when((col(id)==1)&col(value1)==0,(lag(col(value2))+lead(col(value2)))/2.0)\
.when((col(id)==1)&col(col(value1)<0,None).otherwise(col(value2))

此后,我将获取更新的列值,如果再次对id == 2进行相同的操作,则可以获取更新的值。但是我当然不能为每个id都这样做。我该如何实现?

2 个答案:

答案 0 :(得分:0)

DiffUtil

“ final_table”上方将包含您期望的字段。

答案 1 :(得分:0)

我认为完全没有循环执行此操作将很复杂。但是您可以使用udf将工作分散到熊猫的不同执行者和子集中。为了使其正常工作,必须有足够的断点(即,值小于0且您要插入NULL的数据点)。

进口:

from pyspark.sql import Window
from pyspark.sql.functions import last
from pyspark.sql.functions import pandas_udf
from pyspark.sql.functions import PandasUDFType
import pandas as pd
import numpy as np
from pyspark.sql.functions import col, lit, when

输入数据:

df = spark.createDataFrame([[ 1, 7000.0, 30.0 ], [ 2, 0.0, 9.0], [3, 23628.0, 17.0], [4, 8373.0, 23.0], [5, -0.5, 4.0]], [ 'id', 'value1', 'value2' ]).cache()

添加下一个值2并在值小于0时设置断点:

dfwithnextvalue = df.alias("a").join(df.alias("b"), col("a.id") == col("b.id") - lit(1), 'left').select("a.*", col("b.value2").alias("nextvalue"))
dfstartnew = dfwithnextvalue.withColumn("startnew", when(col("value1") < lit(0), col("id")).otherwise(lit(None)))\
.withColumn("startnew", when(col("id") == lit(1), lit(1)).otherwise(col("startnew")))
window = Window.orderBy('id')
rolled = last(col('startnew'), ignorenulls=True).over(window)
dfstartnewrolled = dfstartnew.withColumn("startnew", rolled)

现在,我们可以按startnew列进行分组并处理大熊猫中的每一片。我的熊猫知识不是很好,但这似乎行得通:

@pandas_udf("id long, value1 double, value2 double", PandasUDFType.GROUPED_MAP)
def loopdata(df):
  df = df.set_index('id').sort_index()
  for i in range(0, len(df.index)):
    if i == 0:
      df.loc[df.index[0], 'value2'] = np.nan
    elif df.loc[df.index[i], 'value1'] < 0:
      df.loc[df.index[i], 'value2'] = np.nan
    elif df.loc[df.index[i], 'value1'] > 0:
      df.loc[df.index[i], 'value2'] = df.loc[df.index[i-1], 'value2']
    else:
      nextvalue = df.loc[df.index[i], 'nextvalue']
      if pd.isna(nextvalue):
        nextvalue = 0
      prevvalue = df.loc[df.index[i-1], 'value2']
      if pd.isna(prevvalue):
        prevvalue = 0
      df.loc[df.index[i], 'value2'] = (nextvalue + prevvalue)/2.0
  df = df.drop(columns=['nextvalue', 'startnew'])
  df = df.reset_index()
  return df

现在您可以计算出结果:

dfstartnewrolled.groupBy("startnew").apply(loopdata)