Pandas:每个日期的频率按列以列表的形式分组

时间:2018-03-11 17:12:20

标签: python string list pandas pandas-groupby

我想从熊猫数据框中获取每个日期的技术频率。一个可重复的例子:

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:不可用类型:' list'

所以我认为不可能按列表分组。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您可以使用:

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

答案 2 :(得分:0)

汇总@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)

摘要

总之,所有提出的方法都带来了相同的结果。就执行时间而言,最快的是第二个解决方案,它利用从MultiLabelBinarizer导入的sklearn函数。