Python 如何从包含列表的pandas列中进行热编码?

Python 如何从包含列表的pandas列中进行热编码?,python,pandas,numpy,scikit-learn,sklearn-pandas,Python,Pandas,Numpy,Scikit Learn,Sklearn Pandas,我想将包含元素列表的pandas列分解为具有唯一元素的尽可能多的列,即一个热编码它们(值1表示一行中存在的给定元素,如果不存在则0) 例如,采用数据帧df Col1 Col2 Col3 C 33 [Apple, Orange, Banana] A 2.5 [Apple, Grape] B 42 [Banana] 我想将此转换为: df Col1 Col2 Apple Orange Banana

我想将包含元素列表的pandas列分解为具有唯一元素的尽可能多的列,即
一个热编码
它们(值
1
表示一行中存在的给定元素,如果不存在则
0

例如,采用数据帧df

Col1   Col2         Col3
 C      33     [Apple, Orange, Banana]
 A      2.5    [Apple, Grape]
 B      42     [Banana] 
我想将此转换为:

df

Col1   Col2   Apple   Orange   Banana   Grape
 C      33     1        1        1       0
 A      2.5    1        0        0       1
 B      42     0        0        1       0

我如何使用pandas/sklearn来实现这一点?

使用
获取假人

df_out = df.assign(**pd.get_dummies(df.Col3.apply(lambda x:pd.Series(x)).stack().reset_index(level=1,drop=True)).sum(level=0))
输出:

  Col1  Col2                     Col3  Apple  Banana  Grape  Orange
0    C  33.0  [Apple, Orange, Banana]      1       1      0       1
1    A   2.5           [Apple, Grape]      1       0      1       0
2    B  42.0                 [Banana]      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0
清除列:

df_out.drop('Col3',axis=1)
输出:

  Col1  Col2                     Col3  Apple  Banana  Grape  Orange
0    C  33.0  [Apple, Orange, Banana]      1       1      0       1
1    A   2.5           [Apple, Grape]      1       0      1       0
2    B  42.0                 [Banana]      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0

您可以使用
apply
循环查看
Col3
,并将每个元素转换为一个系列,列表作为索引,成为结果数据框中的标题:

pd.concat([
        df.drop("Col3", 1),
        df.Col3.apply(lambda x: pd.Series(1, x)).fillna(0)
    ], axis=1)

#Col1   Col2    Apple   Banana  Grape   Orange
#0  C   33.0      1.0      1.0    0.0     1.0
#1  A    2.5      1.0      0.0    1.0     0.0
#2  B   42.0      0.0      1.0    0.0     0.0

使用集合理解,您可以在
Col3
中获得所有独特的结果,如下所示:

set(fruit for fruits in df.Col3 for fruit in fruits)
使用字典理解,你可以浏览每一个独特的水果,看看它是否在列中

>>> df[['Col1', 'Col2']].assign(**{fruit: [1 if fruit in cell else 0 for cell in df.Col3] 
                                   for fruit in set(fruit for fruits in df.Col3 
                                                    for fruit in fruits)})
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0
计时

dfs = pd.concat([df] * 1000)  # Use 3,000 rows in the dataframe.

# Solution 1 by @Alexander (me)
%%timeit -n 1000 
dfs[['Col1', 'Col2']].assign(**{fruit: [1 if fruit in cell else 0 for cell in dfs.Col3] 
                                for fruit in set(fruit for fruits in dfs.Col3 for fruit in fruits)})
# 10 loops, best of 3: 4.57 ms per loop

# Solution 2 by @Psidom
%%timeit -n 1000
pd.concat([
        dfs.drop("Col3", 1),
        dfs.Col3.apply(lambda x: pd.Series(1, x)).fillna(0)
    ], axis=1)
# 10 loops, best of 3: 748 ms per loop

# Solution 3 by @MaxU
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()

%%timeit -n 10 
dfs.join(pd.DataFrame(mlb.fit_transform(dfs.Col3),
                          columns=mlb.classes_,
                          index=dfs.index))
# 10 loops, best of 3: 283 ms per loop

# Solution 4 by @ScottBoston
%%timeit -n 10
df_out = dfs.assign(**pd.get_dummies(dfs.Col3.apply(lambda x:pd.Series(x)).stack().reset_index(level=1,drop=True)).sum(level=0))
# 10 loops, best of 3: 512 ms per loop

But...
>>> print(df_out.head())
  Col1  Col2                     Col3  Apple  Banana  Grape  Orange
0    C  33.0  [Apple, Orange, Banana]   1000    1000      0    1000
1    A   2.5           [Apple, Grape]   1000       0   1000       0
2    B  42.0                 [Banana]      0    1000      0       0
0    C  33.0  [Apple, Orange, Banana]   1000    1000      0    1000
1    A   2.5           [Apple, Grape]   1000       0   1000       0
我们还可以使用:

通常,我们希望对真实世界的数据使用稀疏数据帧,以节省大量RAM

稀疏解决方案(适用于v0.25.0+) 结果:

In [38]: df
Out[38]:
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0

In [39]: df.dtypes
Out[39]:
Col1                object
Col2               float64
Apple     Sparse[int32, 0]
Banana    Sparse[int32, 0]
Grape     Sparse[int32, 0]
Orange    Sparse[int32, 0]
dtype: object

In [40]: df.memory_usage()
Out[40]:
Index     128
Col1       24
Col2       24
Apple      16    #  <--- NOTE!
Banana     16    #  <--- NOTE!
Grape       8    #  <--- NOTE!
Orange      8    #  <--- NOTE!
dtype: int64
结果:

In [77]: df
Out[77]:
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0


选项1
简短回答
pir_slow

df.drop('Col3', 1).join(df.Col3.str.join('|').str.get_dummies())

  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0

选项2
快速回答
pir\u fast

v = df.Col3.values
l = [len(x) for x in v.tolist()]
f, u = pd.factorize(np.concatenate(v))
n, m = len(v), u.size
i = np.arange(n).repeat(l)

dummies = pd.DataFrame(
    np.bincount(i * m + f, minlength=n * m).reshape(n, m),
    df.index, u
)

df.drop('Col3', 1).join(dummies)

  Col1  Col2  Apple  Orange  Banana  Grape
0    C  33.0      1       1       1      0
1    A   2.5      1       0       0      1
2    B  42.0      0       0       1      0

选项3
pir_alt1

df.drop('Col3', 1).join(
    pd.get_dummies(
        pd.DataFrame(df.Col3.tolist()).stack()
    ).astype(int).sum(level=0)
)

  Col1  Col2  Apple  Orange  Banana  Grape
0    C  33.0      1       1       1      0
1    A   2.5      1       0       0      1
2    B  42.0      0       0       1      0

计时结果
代码如下


您可以使用功能(0.25.0版中的新增功能)和:

输出:

  Col1  Col2                     Col3  Apple  Banana  Grape  Orange
0    C  33.0  [Apple, Orange, Banana]      1       1      0       1
1    A   2.5           [Apple, Grape]      1       0      1       0
2    B  42.0                 [Banana]      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0
  Col1  Col2  Apple  Banana  Grape  Orange
0    C  33.0      1       1      0       1
1    A   2.5      1       0      1       0
2    B  42.0      0       1      0       0


+1用于将
**
get_dummies
一起使用,但对于大型数据帧,这可能会很慢,因为
.stack()
和方法链接。@BradSolomon谢谢。我不确定这是否有效。。。在:
df=pd.concat([df,df])
之后尝试一下,您可能会发现计时很有趣。这似乎非常消耗内存。我的160 GiB机器内存不足,有1000000行30000列。@DawidLaszuk,尝试使用
MultiLabelBinarizer(sparse\u output=True)
@MaxU是的,我的错,问题不在于MLB,而在于pandas本身(或者更可能是我使用它)。对于测试,可能需要找到一种方法来丢弃100个最常见值之外的条目。@DawidLaszuk,我认为有必要提出一个新问题,提供一个小的可复制样本数据集和您想要的数据集……这真是太棒了!PS我刚刚用了今天最后一次投票;-)太快了!喜欢你的时间表。我假设x轴是数据帧中的行数?@Alexander thx,x轴是
df
的倍数。。。他懒得贴标签。所以1000是pd.concat([df]*1000,忽略_index=True)刚刚在代码中看到了这一点。感谢您的澄清。@Alexander我是一个坚持匹配输出以获得苹果对苹果的人。这个答案应该更受欢迎。。。谢谢这个简洁的解决方案!我的某些行的列表为空,应用上述代码后,新列将获得NaN值。有没有办法将Nan设置为0?这是迄今为止最清晰的答案,但我无法解开df的堆栈。它不太大。@harshpoddar您可以使用
fillna(0)
。谢谢您提供的精彩解决方案
df1
似乎是一个
pd.Series
,而不是
pd.DataFrame
。只是想留下来听听,以防名字
df1
让人困惑。