Pandas:以列表形式按列分组的每个日期的频率
Posted
技术标签:
【中文标题】Pandas:以列表形式按列分组的每个日期的频率【英文标题】:Pandas: frequencies per date grouped by column in a form of a list 【发布时间】:2018-08-19 17:42:25 【问题描述】:我想从 pandas 数据框中获取每个日期的技术频率。一个可重现的例子:
data = pd.DataFrame(
'dates': ['2017-01-31', '2017-02-28', '2017-02-28'],
'tech': [['c++', 'python'], ['c++', 'c', 'java'], ['java']]
)
最终结果可能如下所示(或者名称在行和一列中,每个日期和技术的计数):
date c++ python c java
2017-01-31 1 1 0 0
2017-02-28 1 0 1 2
数据应按其分组的第二列是技术列表。只需尝试按当前状态的数据进行分组:
data.groupby(['dates', data.tech.values]).count()
产生错误:
TypeError: unhashable type: 'list'
所以我认为不可能按列表分组。
【问题讨论】:
【参考方案1】:看来你需要get_dummies
pd.get_dummies(data.set_index('dates').tech.apply(pd.Series).stack()).sum(level=0)
Out[193]:
c c++ java python
dates
2017-01-31 0 1 0 1
2017-02-28 1 1 2 0
或sklearn
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
pd.DataFrame(mlb.fit_transform(data.tech), data.dates, mlb.classes_).sum(level=0)
Out[209]:
c c++ java python
dates
2017-01-31 0 1 0 1
2017-02-28 1 1 2 0
【讨论】:
【参考方案2】:你可以使用:
df1 = (pd.DataFrame(data['tech'].values.tolist(), index=data['dates'].values)
.stack()
.groupby(level=0)
.value_counts()
.unstack(fill_value=0)
)
print (df1)
c c++ java python
2017-01-31 0 1 0 1
2017-02-28 1 1 2 0
解释:
首先由lists
的构造器创建新的DataFrame:
print (pd.DataFrame(data['tech'].values.tolist(), index=data['dates'].values))
0 1 2
2017-01-31 c++ python None
2017-02-28 c++ c java
2017-02-28 java None None
然后通过stack
重塑为Series
:
df1 = (pd.DataFrame(data['tech'].values.tolist(), index=data['dates'].values)
.stack()
)
print (df1)
2017-01-31 0 c++
1 python
2017-02-28 0 c++
1 c
2 java
0 java
dtype: object
通过SeriesGroupBy.value_counts
按第一级获取每组的计数:
df1 = (pd.DataFrame(data['tech'].values.tolist(), index=data['dates'].values)
.stack()
.groupby(level=0)
.value_counts()
)
print (df1)
2017-01-31 c++ 1
python 1
2017-02-28 java 2
c 1
c++ 1
dtype: int64
最后由unstack
重塑为最终DataFrame
:
df1 = (pd.DataFrame(data['tech'].values.tolist(), index=data['dates'].values)
.stack()
.groupby(level=0)
.value_counts()
.unstack(fill_value=0)
)
print (df1)
c c++ java python
2017-01-31 0 1 0 1
2017-02-28 1 1 2 0
【讨论】:
【参考方案3】:总结@Wen 和@jezrael 提供的答案(非常感谢!)我决定通过将连续方法包装到函数中来检查建议解决方案的性能:
数据集
data = pd.DataFrame(
'dates': ['2017-01-31', '2017-02-28', '2017-02-28'],
'tech': [['c++', 'python'], ['c++', 'c', 'java'], ['java']]
)
第一个解决方案 - 假人
def first():
pd.get_dummies(data.set_index('dates').tech.apply(pd.Series).stack()).sum(level = 0)
第二种解决方案 - sklearn
from sklearn.preprocessing import MultiLabelBinarizer
def second():
mlb = MultiLabelBinarizer()
pd.DataFrame(mlb.fit_transform(data.tech), data.dates, mlb.classes_).sum(level = 0)
第三种解决方案 - tolist()
def third():
(pd.DataFrame(data['tech'].values.tolist(), index = data['dates'].values)
.stack()
.groupby(level = 0)
.value_counts()
.unstack(fill_value = 0))
性能对比
%timeit first() # 4.86 ms ± 102 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit second() # 2.09 ms ± 29 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit third() # 3.66 ms ± 256 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
总结
总而言之,所有提出的方法都带来了相同的结果。在执行时间方面,最快的是第二种方案,它利用了从sklearn
导入的MultiLabelBinarizer
函数。
【讨论】:
以上是关于Pandas:以列表形式按列分组的每个日期的频率的主要内容,如果未能解决你的问题,请参考以下文章