如何从包含列表的 Pandas 列中进行独热编码?

2025-02-25 09:07:00
admin
原创
25
摘要:问题描述:我想将由元素列表组成的 pandas 列分解为与唯一元素数量相同的列(即one-hot-encode它们)(其值1表示行中存在的给定元素以及0不存在的情况)。例如,取数据框dfCol1 Col2 Col3 C 33 [Apple, Orange, Banana...

问题描述:

我想将由元素列表组成的 pandas 列分解为与唯一元素数量相同的列(即one-hot-encode它们)(其值1表示行中存在的给定元素以及0不存在的情况)。

例如,取数据框df

Col1   Col2         Col3
 C      33     [Apple, Orange, Banana]
 A      2.5    [Apple, Grape]
 B      42     [Banana] 

我想将其转换为:

自由度

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 来实现这一点?


解决方案 1:

我们还可以使用sklearn.preprocessing.MultiLabelBinarizer:

为了节省大量的 RAM,我们常常想使用稀疏DataFrame 来存储真实世界的数据。

稀疏解决方案(适用于 Pandas v0.25.0+)

from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer(sparse_output=True)

df = df.join(
            pd.DataFrame.sparse.from_spmatrix(
                mlb.fit_transform(df.pop('Col3')),
                index=df.index,
                columns=mlb.classes_))

结果:

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

稠密溶液

mlb = MultiLabelBinarizer()
df = df.join(pd.DataFrame(mlb.fit_transform(df.pop('Col3')),
                          columns=mlb.classes_,
                          index=df.index))

结果:

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

解决方案 2:

选项 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_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(), df.index).stack()
    ).astype(int).groupby(level=0).sum()
)

  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

计时结果

代码如下

在此处输入图片描述


def maxu(df):
    mlb = MultiLabelBinarizer()
    d = pd.DataFrame(
        mlb.fit_transform(df.Col3.values)
        , df.index, mlb.classes_
    )
    return df.drop('Col3', 1).join(d)


def bos(df):
    return df.drop('Col3', 1).assign(**pd.get_dummies(df.Col3.apply(lambda x:pd.Series(x)).stack().reset_index(level=1,drop=True)).sum(level=0))

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

def alex(df):
    return 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)})

def pir_slow(df):
    return df.drop('Col3', 1).join(df.Col3.str.join('|').str.get_dummies())

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

def pir_fast(df):
    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
    )

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

results = pd.DataFrame(
    index=(1, 3, 10, 30, 100, 300, 1000, 3000),
    columns='maxu bos psi alex pir_slow pir_fast pir_alt1'.split()
)

for i in results.index:
    d = pd.concat([df] * i, ignore_index=True)
    for j in results.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        results.set_value(i, j, timeit(stmt, setp, number=10))

解决方案 3:

方法 1使用explode(版本 0.25.0 中的新功能)和crosstab

s = df['Col3'].explode()
df[['Col1', 'Col2']].join(pd.crosstab(s.index, s))

或者在 Python 3.7+ 中:

df[['Col1', 'Col2']].join(pd.crosstab((s:=df['Col3'].explode()).index, s))

方法 2使用isin:

from itertools import chain

lst = sorted(set(chain.from_iterable(df['Col3'])))
s = pd.Series(lst, index=lst)
df.join(df.pop('Col3').apply(lambda x: s.isin(x)).astype(int))

输出:

  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

解决方案 4:

使用get_dummies

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

清理柱:

df_out.drop('Col3',axis=1)

输出:

  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

解决方案 5:

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

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

解决方案 6:

您可以使用集合推导来获取所有独特的水果,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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1344  
  随着房地产行业的快速发展,项目管理在确保项目按时、按预算和高质量完成方面变得至关重要。2025年,房地产项目经理将面临更加复杂的项目需求和更高的管理标准。为了应对这些挑战,选择合适的管理工具显得尤为重要。本文将推荐10款在2025年备受房地产项目经理青睐的管理工具,帮助您提升项目管理效率,确保项目成功。禅道项目管理软件...
开源项目管理工具   0  
  在全球化日益深入的今天,跨国团队的合作已成为企业运营的常态。随着团队成员分布在不同国家和地区,语言和文化的多样性给项目管理带来了新的挑战。为了确保项目顺利进行,选择一款适合的多语言项目管理工具至关重要。本文将介绍2025年跨国团队必备的10款多语言项目管理工具,帮助团队高效协作,提升项目成功率。禅道项目管理软件禅道项目...
项目管理平台   0  
  随着工程管理领域的不断发展,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的重要工具。2025年,工程管理将面临更加复杂的挑战,包括跨团队协作、资源优化、数据驱动决策等。因此,选择一款适合的项目管理软件显得尤为重要。本文将为您推荐2025年最适合工程管理的10款项目管理软件,帮助您在众多选择中找到最适合...
项目管理工具   0  
  跨文化团队的项目运作日益普遍,高级项目管理师在其中扮演着至关重要的角色。有效的沟通是确保项目顺利推进、团队协作无间的关键因素。高级项目管理师需要掌握一系列独特的沟通技巧,以跨越文化差异带来的障碍,实现高效的信息传递与团队协作。理解文化差异文化差异是跨文化团队沟通中面临的首要挑战。不同国家、地区和民族有着各自独特的价值观...
项目管理包括哪些内容   1  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用