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:以列表形式按列分组的每个日期的频率的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 按列分组并检查多个条件以创建新的分类列

如何在 Python 中使用 Pandas 按列分组

Python Pandas:按日期分组,并按时间戳访问每个组

如何在 Pandas 数据框中按列值分组

使用pandas按列分组,然后根据条件新建一列

如何按列对pyspark中的数据框进行分组并以该列作为键并以记录列表作为其值来获取字典?