Dataframe PySpark:用另一个数据帧的值填充一个数据帧的多列中缺少的值

Dataframe PySpark:用另一个数据帧的值填充一个数据帧的多列中缺少的值,dataframe,pyspark,Dataframe,Pyspark,我有一个数据帧(D1),如下所示: col1 | col2 | col3 | col4 22 | null | 23 | 56 12 | 54 | 22 | 36 48 | null | null | 45 null | 32 | 13 | 6 23 | null | 43 | 8 67 | 54 | 56 | null null | 32 | 32 | 6 3 | 54 | 64 | 8 67 |

我有一个数据帧(D1),如下所示:

col1 | col2 | col3 | col4 
22   | null | 23   |  56
12   |  54  | 22   |  36
48   | null | null |  45
null | 32   | 13   |  6
23   | null | 43   |  8
67   | 54   | 56   |  null
null | 32   | 32   |  6
3    | 54   | 64   |  8
67   | 4    | 23   |  null
另一数据帧(D2):

我想用D2中对应于每列的值替换D1中每列中的空值

因此,预期产出为:

col1 | col2 | col3 | col4 
22   | 26   | 23   |  56
12   |  54  | 22   |  36
48   | 26   | 38   |  45
15   | 32   | 13   |  6
23   | 26   | 43   |  8
67   | 54   | 56   |  41
15   | 32   | 32   |  6
3    | 54   | 64   |  8
67   | 4    | 23   |  41
我想知道如何在PySpark数据帧中实现这一点。
干杯

这是一种方法,但由于我们正在使用,它可能不是最有效的方法,但由于
D2
相对较小,因此应该可以。其他方法可以是
udf

# Creating the DataFrame
values = [(22,None,23,56),(12,54,22,36),(48,None,None,45),
(None,32,13,6),(23,None,43,8),(67,54,56,None),
(None,32,32,6),(3,54,64,8),(67,4,23,None)]
D1 = sqlContext.createDataFrame(values,['col1','col2','col3','col4'])
D1.show()
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|  22|null|  23|  56|
|  12|  54|  22|  36|
|  48|null|null|  45|
|null|  32|  13|   6|
|  23|null|  43|   8|
|  67|  54|  56|null|
|null|  32|  32|   6|
|   3|  54|  64|   8|
|  67|   4|  23|null|
+----+----+----+----+
我们需要迭代的列列表,下面的代码给出了这一点

list_columns = D1.columns
print(list_columns)
    ['col1', 'col2', 'col3', 'col4']
创建第二个数据帧

D2 = sqlContext.createDataFrame([('col1',15),('col2',26),('col3',38),('col4',41)],['col_name','value'])
D2.show()
+--------+-----+
|col_name|value|
+--------+-----+
|    col1|   15|
|    col2|   26|
|    col3|   38|
|    col4|   41|
+--------+-----+
让我们将数据框D2添加到数据框中,这样我们就可以沿着所有列追加它

#Pivoting and then renaming the column
D2_new = D2.groupBy().pivot('col_name').sum('value')
D2_new = D2_new.select(*[col(c).alias(c+'_x') for c in D2_new.columns])
D2_new.show()
+------+------+------+------+
|col1_x|col2_x|col3_x|col4_x|
+------+------+------+------+
|    15|    26|    38|    41|
+------+------+------+------+
最后使用交叉连接,我们附加它们-

# Appending the columns
D1 = D1.crossJoin(D2_new)
D1.show()
+----+----+----+----+------+------+------+------+
|col1|col2|col3|col4|col1_x|col2_x|col3_x|col4_x|
+----+----+----+----+------+------+------+------+
|  22|null|  23|  56|    15|    26|    38|    41|
|  12|  54|  22|  36|    15|    26|    38|    41|
|  48|null|null|  45|    15|    26|    38|    41|
|null|  32|  13|   6|    15|    26|    38|    41|
|  23|null|  43|   8|    15|    26|    38|    41|
|  67|  54|  56|null|    15|    26|    38|    41|
|null|  32|  32|   6|    15|    26|    38|    41|
|   3|  54|  64|   8|    15|    26|    38|    41|
|  67|   4|  23|null|    15|    26|    38|    41|
+----+----+----+----+------+------+------+------+
一旦获得了这个主
DataFrame
,我们就可以通过在列列表上运行循环来使用简单的构造进行替换

# Finally doing the replacement.
for c in list_columns:
    D1 = D1.withColumn(c,when(col(c).isNull(),col(c+'_x')).otherwise(col(c))).drop(col(c+'_x'))
D1.show()
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|  22|  26|  23|  56|
|  12|  54|  22|  36|
|  48|  26|  38|  45|
|  15|  32|  13|   6|
|  23|  26|  43|   8|
|  67|  54|  56|  41|
|  15|  32|  32|   6|
|   3|  54|  64|   8|
|  67|   4|  23|  41|
+----+----+----+----+

IIUC,您可以创建一个
列\u name:value
映射,然后只需对每列执行
fillna()

mapping = { row.col_name.replace(' ',''):row.value for row in D2.collect() }
#{u'col1': 15.0, u'col2': 26.0, u'col3': 38.0, u'col4': 41.0}

# fillna on col1 for testing
D1.fillna(mapping['col1'], subset=['col1']).show()
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|22.0| NaN|23.0|56.0|
|12.0|54.0|22.0|36.0|
|48.0| NaN| NaN|45.0|
|15.0|32.0|13.0| 6.0|
|23.0| NaN|43.0| 8.0|
|67.0|54.0|56.0| NaN|
|15.0|32.0|32.0| 6.0|
| 3.0|54.0|64.0| 8.0|
|67.0| 4.0|23.0| NaN|
+----+----+----+----+

# use a reduce function to handle all columns
df_new =  reduce(lambda d,c: d.fillna(mapping[c], subset=[c]), D1.columns, D1)
或者使用列表理解

from pyspark.sql.functions import isnan, when, col

df_new = D1.select([ when(isnan(c), mapping[c]).otherwise(col(c)).alias(c) for c in D1.columns ])

注意:对于StringType列,将上面的
isnan()
替换为
isnull()

问题,与本文@jxc相同在这个特定示例中,isNotNull()似乎不起作用,为什么?
from pyspark.sql.functions import isnan, when, col

df_new = D1.select([ when(isnan(c), mapping[c]).otherwise(col(c)).alias(c) for c in D1.columns ])