如何对 pyspark 使用单热编码或 get_dummies 并将列表作为列中的值?

How to use one-hot encoding or get_dummies for pyspark with lists as values in column?

我有一个 spark 数据框:

+-----------+--------------------+
|columnIndex|               lists|
+-----------+--------------------+
|          1|[1.0,2.0]           |
|          2|[2.0]               |
|          3|[1.0]               |
|          4|[1.0,3.0]           |
+-----------+--------------------+

我需要在one-hot编码后得到以下内容或者get_dummies:

+-----------+--------------------+---+---+---+
|columnIndex|               lists|1.0|2.0|3.0|
+-----------+--------------------+---+---+---+
|          1|[1.0,2.0]           |  1|  1|  0|
|          2|[2.0]               |  0|  1|  0|
|          3|[1.0]               |  1|  0|  0|
|          4|[1.0,3.0]           |  1|  0|  1|
+-----------+--------------------+---+---+---+

我试过 CountVectorizer(),但无法获得所需的输出。此示例是我拥有的数据的示例。

下面是 one-hot 从一列类别列表中编码的解决方案。

在Pandas

import pandas as pd

data = {'col1': list(range(1,5)), 'lists': [[1.0,2.0], [2.0],[1.0],[1.0,3.0]]}
df = pd.DataFrame.from_dict(data)
df
# Out:
#    col1       lists
# 0     1  [1.0, 2.0]
# 1     2       [2.0]
# 2     3       [1.0]
# 3     4  [1.0, 3.0]

s = df['lists'].explode()
df[['col1']].join(pd.crosstab(s.index, s))
# Out: 
#    col1  1.0  2.0  3.0
# 0     1    1    1    0
# 1     2    0    1    0
# 2     3    1    0    0
# 3     4    1    0    1

在 Pyspark 中

from pyspark.sql import SparkSession
from pyspark.sql.functions import *

spark = SparkSession.builder.getOrCreate()
sparkDF = spark.createDataFrame(df) # spark is the Spark session
sparkDF.show()
# Out:
# +----+----------+
# |col1|     lists|
# +----+----------+
# |   1|[1.0, 2.0]|
# |   2|     [2.0]|
# |   3|     [1.0]|
# |   4|[1.0, 3.0]|
# +----+----------+

sparkDF2 = sparkDF.select(sparkDF.col1,explode(sparkDF.lists).alias('newcol'))
sparkDF2.show()
# Out:
# +----+------+
# |col1|newcol|
# +----+------+
# |   1|   1.0|
# |   1|   2.0|
# |   2|   2.0|
# |   3|   1.0|
# |   4|   1.0|
# |   4|   3.0|
# +----+------+

sparkDF.join(sparkDF2.crosstab('col1', 'newcol').withColumnRenamed('col1_newcol','col1'), 'col1').show()
# Out:
# +----+----------+---+---+---+
# |col1|     lists|1.0|2.0|3.0|
# +----+----------+---+---+---+
# |   1|[1.0, 2.0]|  1|  1|  0|
# |   2|     [2.0]|  0|  1|  0|
# |   3|     [1.0]|  1|  0|  0|
# |   4|[1.0, 3.0]|  1|  0|  1|
# +----+----------+---+---+---+

请注意,这假设列表包含唯一类别,如果列表中重复了一个类别,那么由于 crosstab.

,它的计数将会出现

例如,如果sparkDF是

+----+---------------+
|col1|          lists|
+----+---------------+
|   1|     [1.0, 2.0]|
|   2|          [2.0]|
|   3|          [1.0]|
|   4|[1.0, 3.0, 3.0]|
+----+---------------+
           ^^^^^^^^^^

那么结果是:

+----+---------------+---+---+---+
|col1|          lists|1.0|2.0|3.0|
+----+---------------+---+---+---+
|   1|     [1.0, 2.0]|  1|  1|  0|
|   2|          [2.0]|  0|  1|  0|
|   3|          [1.0]|  1|  0|  0|
|   4|[1.0, 3.0, 3.0]|  1|  0|  2|
+----+---------------+---+---+---+
                              ^^^^

这可以通过简单的转换来调整。

import more_itertools as mit
import numpy as np
import pandas as pd

df = pd.DataFrame({'columnIndex': [1, 2, 3, 4], 'lists': [[1.0, 2.0], [2.0], [1.0], [1.0, 3.0]]})
df = pd.concat([df, pd.DataFrame(columns=[1, 2, 3])])
df[[1, 2, 3]] = [0, 0, 0]

for i in range(0, len(df['lists'])):
    index = list(mit.locate([1, 2, 3], lambda x: x in df.loc[i, 'lists']))
    index = np.array(index) + 1
    df.loc[i, index] = 1

输出

   columnIndex       lists  1  2  3
0          1.0  [1.0, 2.0]  1  1  0
1          2.0       [2.0]  0  1  0
2          3.0       [1.0]  1  0  0
3          4.0  [1.0, 3.0]  1  0  1

我可以提供基于 pandas 的选项。首先,创建值为 0 的缺失列。此外,基于列号,获得用于设置值的索引。请注意,将 1 添加到索引中。