拆分以竖线分隔的系列,按单独的系列分组,并在新列中返回每个拆分值的计数

Big*_*Ben 5 python matplotlib pandas

给定一个带有竖线分隔系列的数据框:

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({'year': [1960, 1960, 1961, 1961, 1961],
                   'genre': ['Drama|Romance|Thriller',
                             'Spy|Mystery|Bio',
                             'Drama|Romance',
                             'Drama|Romance',
                             'Drama|Spy']})
Run Code Online (Sandbox Code Playgroud)

或以数据格式:

   year                   genre
0  1960  Drama|Romance|Thriller
1  1960         Spy|Mystery|Bio
2  1961           Drama|Romance
3  1961           Drama|Romance
4  1961               Drama|Spy
Run Code Online (Sandbox Code Playgroud)

我可以将genre系列拆分为str.split(如关于 SO 的许多类似问题所示)。

但是,我也想在今年组和返回的计数DramaRomanceThriller,等了新列,每列特殊的一年。

我的初步尝试:

df_split = df.groupby('year')['genre'].apply(lambda x: x.str.split('|', expand=True).reset_index(drop=True))
Run Code Online (Sandbox Code Playgroud)

返回

            0        1         2
year                            
1960 0  Drama  Romance  Thriller
     1    Spy  Mystery       Bio
1961 0  Drama  Romance       NaN
     1  Drama  Romance       NaN
     2  Drama      Spy       NaN
Run Code Online (Sandbox Code Playgroud)

但是如何按年份在其自己的列中获得每个独特类型的数量?

我可以使用独特的流派

genres = pd.unique(df['genre'].str.split('|', expand=True).stack())
Run Code Online (Sandbox Code Playgroud)

但我仍然不确定如何将这些类型作为单独的系列,按年份进行计数。

我想要的最终输出是:

      Drama  Romance  Thriller  Spy  Mystery  Bio
1960      1        1         1    1        1    1
1961      3        2         0    1        0    0
Run Code Online (Sandbox Code Playgroud)

每个独特的流派都是它自己的系列,按年份计算相应的数量。

这也很可能是 XY 问题。我的最终目标是制作一个百分比堆积面积图。假设df_split有所需的转换,我想做:

df_perc = df_split.divide(df_split.sum(axis=1), axis=0)
Run Code Online (Sandbox Code Playgroud)

返回

         Drama   Romance  Thriller       Spy   Mystery       Bio
1960  0.166667  0.166667  0.166667  0.166667  0.166667  0.166667
1961  0.500000  0.333333  0.000000  0.166667  0.000000  0.000000
Run Code Online (Sandbox Code Playgroud)

进而

plt.stackplot(df_perc.index, *[ts for col, ts in df_perc.iteritems()],
                               labels=df_perc.columns)
plt.gca().set_xticks(df_perc.index)
plt.margins(0)
plt.legend()
Run Code Online (Sandbox Code Playgroud)

给出输出:

在此处输入图片说明

cs9*_*s95 4

我们可以使用一些简单的重塑和聚合来获得您想要的结果:

(df.assign(genre=df['genre'].str.split('|'))
   .explode('genre')
   .groupby('year')['genre']
   .value_counts(normalize=True)
   .unstack(fill_value=0))     
 
genre       Bio     Drama   Mystery   Romance       Spy  Thriller
year                                                             
1960   0.166667  0.166667  0.166667  0.166667  0.166667  0.166667
1961   0.000000  0.500000  0.000000  0.333333  0.166667  0.000000
Run Code Online (Sandbox Code Playgroud)

从这里您可以通过绘制面积图来完成:

(df.assign(genre=df['genre'].str.split('|'))
   .explode('genre')
   .groupby('year')['genre']
   .value_counts(normalize=True)
   .unstack(fill_value=0)
   .plot
   .area())  
Run Code Online (Sandbox Code Playgroud)

怎么运行的

首先跨行分解数据:

df.assign(genre=df['genre'].str.split('|')).explode('genre') 

   year     genre
0  1960     Drama
0  1960   Romance
0  1960  Thriller
1  1960       Spy
1  1960   Mystery
1  1960       Bio
2  1961     Drama
2  1961   Romance
3  1961     Drama
3  1961   Romance
4  1961     Drama
4  1961       Spy
Run Code Online (Sandbox Code Playgroud)

接下来,执行 agroupby并获取标准化计数:

_.groupby('year')['genre'].value_counts(normalize=True)

year  genre   
1960  Bio         0.166667
      Drama       0.166667
      Mystery     0.166667
      Romance     0.166667
      Spy         0.166667
      Thriller    0.166667
1961  Drama       0.500000
      Romance     0.333333
      Spy         0.166667
Name: genre, dtype: float64
Run Code Online (Sandbox Code Playgroud)

接下来,取消堆叠结果:

_.unstack(fill_value=0)

genre       Bio     Drama   Mystery   Romance       Spy  Thriller
year                                                             
1960   0.166667  0.166667  0.166667  0.166667  0.166667  0.166667
1961   0.000000  0.500000  0.000000  0.333333  0.166667  0.000000
Run Code Online (Sandbox Code Playgroud)

最后,绘制

_.plot.area()
Run Code Online (Sandbox Code Playgroud)