我有一个数据框,其中一列是我的每个用户所属的组列表.就像是:
index groups
0 ['a','b','c']
1 ['c']
2 ['b','c','e']
3 ['a','c']
4 ['b','e']
Run Code Online (Sandbox Code Playgroud)
我想要做的是创建一系列虚拟列,以确定每个用户所属的组,以便运行一些分析
index a b c d e
0 1 1 1 0 0
1 0 0 1 0 0
2 0 1 1 0 1
3 1 0 1 0 0
4 0 1 0 0 0
pd.get_dummies(df['groups'])
Run Code Online (Sandbox Code Playgroud)
将无法工作,因为它只返回列中每个不同列表的列.
解决方案需要高效,因为数据帧将包含500,000多行.任何意见,将不胜感激!
jor*_*ris 41
使用s您的df['groups']:
In [21]: s = pd.Series({0: ['a', 'b', 'c'], 1:['c'], 2: ['b', 'c', 'e'], 3: ['a', 'c'], 4: ['b', 'e'] })
In [22]: s
Out[22]:
0 [a, b, c]
1 [c]
2 [b, c, e]
3 [a, c]
4 [b, e]
dtype: object
Run Code Online (Sandbox Code Playgroud)
这是一个可能的解决方案:
In [23]: pd.get_dummies(s.apply(pd.Series).stack()).sum(level=0)
Out[23]:
a b c e
0 1 1 1 0
1 0 0 1 0
2 0 1 1 1
3 1 0 1 0
4 0 1 0 1
Run Code Online (Sandbox Code Playgroud)
这个逻辑是:
.apply(Series) 将一系列列表转换为数据帧.stack() 将所有内容重新放入一列(创建多级索引)pd.get_dummies( ) 创造假人.sum(level=0)重新合并应该是一行的不同行(通过总结第二级,只保留原始级别(level=0))略微相当于 pd.get_dummies(s.apply(pd.Series), prefix='', prefix_sep='').sum(level=0, axis=1)
如果这个效率足够高,我不知道,但无论如何,如果性能很重要,那么在数据帧中存储列表并不是一个好主意.
Teo*_*tic 15
如果您拥有大型数据帧,则可以快速解决问题
使用sklearn.preprocessing.MultiLabelBinarizer
import pandas as pd
from sklearn.preprocessing import MultiLabelBinarizer
df = pd.DataFrame(
{'groups':
[['a','b','c'],
['c'],
['b','c','e'],
['a','c'],
['b','e']]
}, columns=['groups'])
s = df['groups']
mlb = MultiLabelBinarizer()
pd.DataFrame(mlb.fit_transform(s),columns=mlb.classes_, index=df.index)
Run Code Online (Sandbox Code Playgroud)
结果:
a b c e
0 1 1 1 0
1 0 0 1 0
2 0 1 1 1
3 1 0 1 0
4 0 1 0 1
Run Code Online (Sandbox Code Playgroud)
即使这个任务得到了回答,我也有一个更快的解决方案:
df.groups.apply(lambda x: pd.Series([1] * len(x), index=x)).fillna(0, downcast='infer')
Run Code Online (Sandbox Code Playgroud)
而且,如果你有空组或者NaN你可以:
df.loc[df.groups.str.len() > 0].apply(lambda x: pd.Series([1] * len(x), index=x)).fillna(0, downcast='infer')
Run Code Online (Sandbox Code Playgroud)
x例如,在lambda中,是你的列表['a', 'b', 'c'].所以pd.Series将如下:
In [2]: pd.Series([1, 1, 1], index=['a', 'b', 'c'])
Out[2]:
a 1
b 1
c 1
dtype: int64
Run Code Online (Sandbox Code Playgroud)
当所有pd.Series走到一起,他们变得pd.DataFrame和他们index成为columns; 失踪index成了一个column与NaN你可以看到旁边:
In [4]: a = pd.Series([1, 1, 1], index=['a', 'b', 'c'])
In [5]: b = pd.Series([1, 1, 1], index=['a', 'b', 'd'])
In [6]: pd.DataFrame([a, b])
Out[6]:
a b c d
0 1.0 1.0 1.0 NaN
1 1.0 1.0 NaN 1.0
Run Code Online (Sandbox Code Playgroud)
现在,fillna填充那些NaN有0:
In [7]: pd.DataFrame([a, b]).fillna(0)
Out[7]:
a b c d
0 1.0 1.0 1.0 0.0
1 1.0 1.0 0.0 1.0
Run Code Online (Sandbox Code Playgroud)
而downcast='infer'从向下转型float到int:
In [11]: pd.DataFrame([a, b]).fillna(0, downcast='infer')
Out[11]:
a b c d
0 1 1 1 0
1 1 1 0 1
Run Code Online (Sandbox Code Playgroud)
PS.:不需要使用.fillna(0, downcast='infer').
这甚至更快:
pd.get_dummies(df['groups'].explode()).sum(level=0)
使用.explode()代替.apply(pd.Series).stack()
与其他解决方案的比较:
import timeit
import pandas as pd
setup = '''
import time
import pandas as pd
s = pd.Series({0:['a','b','c'],1:['c'],2:['b','c','e'],3:['a','c'],4:['b','e']})
df = s.rename('groups').to_frame()
'''
m1 = "pd.get_dummies(s.apply(pd.Series).stack()).sum(level=0)"
m2 = "df.groups.apply(lambda x: pd.Series([1] * len(x), index=x)).fillna(0, downcast='infer')"
m3 = "pd.get_dummies(df['groups'].explode()).sum(level=0)"
times = {f"m{i+1}":min(timeit.Timer(m, setup=setup).repeat(7, 1000)) for i, m in enumerate([m1, m2, m3])}
pd.DataFrame([times],index=['ms'])
# m1 m2 m3
# ms 5.586517 3.821662 2.547167
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11676 次 |
| 最近记录: |