pandas分组groupby(agg,transform),apply

快速浏览

    • pandas简单介绍和本文说明
    • 一、SAC过程
    • 二、groupby函数
      • 1. groupby分组函数的基本内容
        • 单/多列分组
        • 组容量与组数
        • 组的遍历
        • level参数(用于多级索引)和axis参数
      • 2.groupby对象的特点
        • 分组对象的head和first
        • 分组依据
        • groupby的[与]操作
        • 连续型变量分组
    • 三、聚合、过滤和变换
      • 1. 聚合(Aggregation)
        • (a)常用聚合函数
        • (b)同时使用多个聚合函数
        • (c)使用自定义函数
        • (d)利用NamedAgg函数进行多个聚合
        • (e)带参数的聚合函数
      • 2. 过滤(Filteration)
      • 3. 变换(Transformation)
        • (a)传入对象
        • (b)利用变换方法进行组内标准化
        • (c)利用变换方法进行组内缺失值的均值填充
    • 四、apply函数
      • 1. apply函数的灵活性
      • 2. 用apply同时统计多个指标
    • 问题与练习
    • 参考

pandas简单介绍和本文说明

pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。
pandas is a fast, powerful, flexible and easy to use open source data analysis and manipulation tool,built on top of the Python programming language.

进入官网可以看到现在最新的版本是1.0.3,之后代码也就使用1.0.3的pd咯。此系列主要参考Pandas官方文档和由Datawhale主办的一期Joyful-Pandas,结合自己使用pandas的一些体会进行扩展,最后还有个人对问题和练习的解答。使用到的数据集可以在此下载。
在这里插入图片描述

#从清华镜像拉装1.0.3版本的Pandas
!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas==1.0.3
import pandas as pd
#查看Pandas版本
pd.__version__
'1.0.3'
df = pd.read_csv('work/table.csv',index_col='ID')
df.head()

一、SAC过程

SAC指的是分组操作中的split-apply-combine过程
在该过程中,我们实际往往会遇到四类问题:
整合(Aggregation)——即分组计算统计量(如求均值、求每组元素个数)
变换(Transformation)——即分组对每个单元的数据进行操作(如元素标准化)
过滤(Filtration)——即按照某些规则筛选出一些组(如选出组内某一指标小于50的组)
综合问题——即前面提及的三种问题的混合

二、groupby函数

1. groupby分组函数的基本内容

单/多列分组

经过groupby后会生成一个groupby对象,该对象本身不会返回任何东西,只有当相应的方法被调用才会起作用。

grouped_single = df.groupby('School')
print(grouped_single)

print(grouped_single.get_group('S_1').head())

打印了School为S_1的

     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
1101    S_1   C_1      M  street_1     173      63  34.0      A+
1102    S_1   C_1      F  street_2     192      73  32.5      B+
1103    S_1   C_1      M  street_2     186      82  87.2      B+
1104    S_1   C_1      F  street_2     167      81  80.4      B-
1105    S_1   C_1      F  street_4     159      64  84.8      B+
grouped_mul = df.groupby(['School','Class'])
grouped_mul.get_group(('S_2','C_4'))

根据多列分组

     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
2401    S_2   C_4      F  street_2     192      62  45.3       A
2402    S_2   C_4      M  street_7     166      82  48.7       B
2403    S_2   C_4      F  street_6     158      60  59.7      B+
2404    S_2   C_4      F  street_2     160      84  67.7       B
2405    S_2   C_4      F  street_6     193      54  47.6       B

组容量与组数

print('组容量与组数')
print(grouped_single.size())
print(grouped_mul.size())
print(grouped_single.ngroups)
print(grouped_mul.ngroups)
组容量与组数
School
S_1    15
S_2    20
dtype: int64
School  Class
S_1     C_1      5
        C_2      5
        C_3      5
S_2     C_1      5
        C_2      5
        C_3      5
        C_4      5
dtype: int64
2
7

组的遍历

for name,group in grouped_single:
    print(name)
    print(group)
S_1
     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
1101    S_1   C_1      M  street_1     173      63  34.0      A+
1102    S_1   C_1      F  street_2     192      73  32.5      B+
1103    S_1   C_1      M  street_2     186      82  87.2      B+
1104    S_1   C_1      F  street_2     167      81  80.4      B-
1105    S_1   C_1      F  street_4     159      64  84.8      B+
1201    S_1   C_2      M  street_5     188      68  97.0      A-
1202    S_1   C_2      F  street_4     176      94  63.5      B-
1203    S_1   C_2      M  street_6     160      53  58.8      A+
1204    S_1   C_2      F  street_5     162      63  33.8       B
1205    S_1   C_2      F  street_6     167      63  68.4      B-
1301    S_1   C_3      M  street_4     161      68  31.5      B+
1302    S_1   C_3      F  street_1     175      57  87.7      A-
1303    S_1   C_3      M  street_7     188      82  49.7       B
1304    S_1   C_3      M  street_2     195      70  85.2       A
1305    S_1   C_3      F  street_5     187      69  61.7      B-
S_2
     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
2101    S_2   C_1      M  street_7     174      84  83.3       C
2102    S_2   C_1      F  street_6     161      61  50.6      B+
2103    S_2   C_1      M  street_4     157      61  52.5      B-
2104    S_2   C_1      F  street_5     159      97  72.2      B+
2105    S_2   C_1      M  street_4     170      81  34.2       A
2201    S_2   C_2      M  street_5     193     100  39.1       B
2202    S_2   C_2      F  street_7     194      77  68.5      B+
2203    S_2   C_2      M  street_4     155      91  73.8      A+
2204    S_2   C_2      M  street_1     175      74  47.2      B-
2205    S_2   C_2      F  street_7     183      76  85.4       B
2301    S_2   C_3      F  street_4     157      78  72.3      B+
2302    S_2   C_3      M  street_5     171      88  32.7       A
2303    S_2   C_3      F  street_7     190      99  65.9       C
2304    S_2   C_3      F  street_6     164      81  95.5      A-
2305    S_2   C_3      M  street_4     187      73  48.9       B
2401    S_2   C_4      F  street_2     192      62  45.3       A
2402    S_2   C_4      M  street_7     166      82  48.7       B
2403    S_2   C_4      F  street_6     158      60  59.7      B+
2404    S_2   C_4      F  street_2     160      84  67.7       B
2405    S_2   C_4      F  street_6     193      54  47.6       B

level参数(用于多级索引)和axis参数

参数level,指明聚合的层级

print(df.set_index(['Gender','School']).groupby(level=1,axis=0).get_group('S_1').head()

level就是school,也可以直接用’school’——df.set_index(['Gender','School']).groupby(level='School',axis=0).get_group('S_1').head())

              Class   Address  Height  Weight  Math Physics
Gender School                                              
M      S_1      C_1  street_1     173      63  34.0      A+
F      S_1      C_1  street_2     192      73  32.5      B+
M      S_1      C_1  street_2     186      82  87.2      B+
F      S_1      C_1  street_2     167      81  80.4      B-
       S_1      C_1  street_4     159      64  84.8      B+

2.groupby对象的特点

print([attr for attr in dir(grouped_single) if not attr.startswith('_')])

[‘Address’, ‘Class’, ‘Gender’, ‘Height’, ‘Math’, ‘Physics’, ‘School’, ‘Weight’, ‘agg’, ‘aggregate’, ‘all’, ‘any’, ‘apply’, ‘backfill’, ‘bfill’, ‘boxplot’, ‘corr’, ‘corrwith’, ‘count’, ‘cov’, ‘cumcount’, ‘cummax’, ‘cummin’, ‘cumprod’, ‘cumsum’, ‘describe’, ‘diff’, ‘dtypes’, ‘expanding’, ‘ffill’, ‘fillna’, ‘filter’, ‘first’, ‘get_group’, ‘groups’, ‘head’, ‘hist’, ‘idxmax’, ‘idxmin’, ‘indices’, ‘last’, ‘mad’, ‘max’, ‘mean’, ‘median’, ‘min’, ‘ndim’, ‘ngroup’, ‘ngroups’, ‘nth’, ‘nunique’, ‘ohlc’, ‘pad’, ‘pct_change’, ‘pipe’, ‘plot’, ‘prod’, ‘quantile’, ‘rank’, ‘resample’, ‘rolling’, ‘sem’, ‘shift’, ‘size’, ‘skew’, ‘std’, ‘sum’, ‘tail’, ‘take’, ‘transform’, ‘tshift’, ‘var’]
groupby对象可以使用相当多的函数,灵活程度很高

分组对象的head和first

grouped_single.head(2)对分组对象使用head函数,返回的是每个组的前几行,而不是数据集前几行。
first显示的是以分组为索引的每组的第一个分组信息。grouped_single.first()

分组依据

对于groupby函数而言,分组的依据是非常自由的,只要是与数据框长度相同的列表即可,同时支持函数型分组。

df.groupby(np.random.choice(['a','b','c'],df.shape[0])).get_group('a').head()
#相当于将np.random.choice(['a','b','c'],df.shape[0])当做新的一列进行分组

从原理上说,我们可以看到利用函数时,传入的对象就是索引,因此根据这一特性可以做一些复杂的操作。比如根据奇偶行分组:

df.groupby(lambda x:'奇数行' if not df.index.get_loc(x)%2==1 else '偶数行').groups
{'偶数行': Int64Index([1102, 1104, 1201, 1203, 1205, 1302, 1304, 2101, 2103, 2105, 2202,
             2204, 2301, 2303, 2305, 2402, 2404],
            dtype='int64', name='ID'),
 '奇数行': Int64Index([1101, 1103, 1105, 1202, 1204, 1301, 1303, 1305, 2102, 2104, 2201,
             2203, 2205, 2302, 2304, 2401, 2403, 2405],
            dtype='int64', name='ID')}

m.groups() 返回所有括号匹配的字符,以tuple格式。
m.groups() == (m.group(0), m.group(1), …)
如果是多层索引,那么lambda表达式中的输入就是元组,下面实现的功能为查看两所学校中男女生分别均分是否及格。注意:此处只是演示groupby的用法,实际操作不会这样写。

math_score = df.set_index(['Gender','School'])['Math'].sort_index()
grouped_score = df.set_index(['Gender','School']).sort_index().\
            groupby(lambda x:(x,'均分及格' if math_score[x].mean()>=60 else '均分不及格'))
for name,_ in grouped_score:
	print(name)
(('F', 'S_1'), '均分及格')
(('F', 'S_2'), '均分及格')
(('M', 'S_1'), '均分及格')
(('M', 'S_2'), '均分不及格')

groupby的[与]操作

可以用[]选出groupby对象的某个或者某几个列,上面的均分比较可以如下简洁地写出:

df.groupby(['Gender','School'])['Math'].mean()>=60
Gender  School
F       S_1        True
        S_2        True
M       S_1        True
        S_2       False
Name: Math, dtype: bool

用列表可选出多个属性列:

print(df.groupby(['Gender','School'])[['Math','Height']].mean())
                    Math      Height
Gender School                       
F      S_1     64.100000  173.125000
       S_2     66.427273  173.727273
M      S_1     63.342857  178.714286
       S_2     51.155556  172.000000

连续型变量分组

例如利用cut函数对数学成绩分组

bins = [0,40,60,80,90,100]
cuts = pd.cut(df['Math'],bins=bins) #可选label添加自定义标签
df.groupby(cuts)['Math'].count()
Math
(0, 40]       7
(40, 60]     10
(60, 80]      9
(80, 90]      7
(90, 100]     2
Name: Math, dtype: int64

三、聚合、过滤和变换

小结: 聚合主要还是对每个组,转换使用时是对组内每个对象

1. 聚合(Aggregation)

(a)常用聚合函数

所谓聚合就是把一堆数,变成一个标量,而mean/sum/size/count/std/var/sem/describe/first/last/nth/min/max都是聚合函数。为了熟悉操作,下面验证标准误sem函数,

group_m = grouped_single['Math']
group_m.std().values/np.sqrt(group_m.count().values)== group_m.sem().values
#array([ True,  True])

(b)同时使用多个聚合函数

agg使用率很高,强烈推荐

group_m = grouped_single['Math']
print(group_m.agg(['sum','mean','std']))

print('利用元组进行重命名')
print(group_m.agg([('rename_sum','sum'),('rename_mean','mean')]))
print('指定哪些函数作用哪些列')
print(grouped_mul.agg({'Math':['mean','max'],'Height':'var'}))

           sum       mean        std
School                              
S_1      956.2  63.746667  23.077474
S_2     1191.1  59.555000  17.589305
利用元组进行重命名
        rename_sum  rename_mean
School                         
S_1          956.2    63.746667
S_2         1191.1    59.555000
指定哪些函数作用哪些列
               Math       Height
               mean   max    var
School Class                    
S_1    C_1    63.78  87.2  183.3
       C_2    64.30  97.0  132.8
       C_3    63.16  87.7  179.2
S_2    C_1    58.56  83.3   54.7
       C_2    62.80  85.4  256.0
       C_3    63.06  95.5  205.7
       C_4    53.80  67.7  300.2

(c)使用自定义函数

grouped_single['Math'].agg(lambda x:print(x.head(),'间隔'))
1101    34.0
1102    32.5
1103    87.2
1104    80.4
1105    84.8
Name: Math, dtype: float64 间隔
2101    83.3
2102    50.6
2103    52.5
2104    72.2
2105    34.2
Name: Math, dtype: float64 间隔
School
S_1    None
S_2    None
Name: Math, dtype: object

可以发现,agg函数的传入是分组逐列进行的,有了这个特性就可以做许多事情.
官方没有提供极差计算的函数,但通过agg可以容易地实现组内极差计算

grouped_single['Math'].agg(lambda x:x.max()-x.min())
School
S_1    65.5
S_2    62.8
Name: Math, dtype: float64

(d)利用NamedAgg函数进行多个聚合

NamedAgg不支持lambda函数,但是可以使用外置的def函数
pandas分组groupby(agg,transform),apply_第1张图片

def R1(x):
    return x.max()-x.min()
def R2(x):
    return x.max()-x.median()
grouped_single['Math'].agg(min_score1=pd.NamedAgg(column='col1', aggfunc=R1),
                           max_score1=pd.NamedAgg(column='col2', aggfunc='max'),
                           range_score2=pd.NamedAgg(column='col3', aggfunc=R2)).head()
        min_score1  max_score1  range_score2
School                                      
S_1           65.5        97.0          33.5
S_2           62.8        95.5          39.4

(e)带参数的聚合函数

判断是否组内数学分数至少有一个值在50-52之间:

def f(s,low,high):
    return s.between(low,high).max()
grouped_single['Math'].agg(f,50,52)

如果需要使用多个函数,并且其中至少有一个带参数,则使用wrap技巧:

def f_test(s,low,high):
    return s.between(low,high).max()
def agg_f(f_mul,name,*args,**kwargs):
    def wrapper(x):
        return f_mul(x,*args,**kwargs)
    wrapper.__name__ = name
    return wrapper
new_f = agg_f(f_test,'at_least_one_in_50_52',50,52)
grouped_single['Math'].agg([new_f,'mean']).head()

2. 过滤(Filteration)

filter函数是用来筛选某些组的(务必记住结果是组的全体),因此传入的值应当是布尔标量

print(grouped_single[['Math','Physics']].filter(lambda x:(x['Math']>32).all()).head())
      Math Physics
ID                
2101  83.3       C
2102  50.6      B+
2103  52.5      B-
2104  72.2      B+
2105  34.2       A

3. 变换(Transformation)

这也是非常常用的!!!

(a)传入对象

transform函数中传入的对象是组内的列,并且返回值需要与列长完全一致

grouped_single[['Math','Height']].transform(lambda x:x-x.min()).head()

如果返回了标量值,那么组内的所有元素会被广播(就是大家都一样了)为这个值

grouped_single[['Math','Height']].transform(lambda x:x.mean()).head()

(b)利用变换方法进行组内标准化

grouped_single[['Math','Height']].transform(lambda x:(x-x.mean())/x.std()).head()

(c)利用变换方法进行组内缺失值的均值填充

df_nan = df[['Math','School']].copy().reset_index()
df_nan.loc[np.random.randint(0,df.shape[0],25),['Math']]=np.nan
df_nan.head()

以上处理出有空缺值的表

df_nan.groupby('School').transform(lambda x: x.fillna(x.mean())).join(df.reset_index()['School']).head()

四、apply函数

1. apply函数的灵活性

可能在所有的分组函数中,apply是应用最为广泛的(之前也有多次使用过apply),这得益于它的灵活性。
对于传入值而言,从下面的打印内容可以看到是以分组的表传入apply中:df.groupby('School').apply(lambda x:print(x.head(1)))

     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
1101    S_1   C_1      M  street_1     173      63  34.0      A+
     School Class Gender   Address  Height  Weight  Math Physics
ID                                                              
2101    S_2   C_1      M  street_7     174      84  83.3       C

apply函数的灵活性很大程度来源于其返回值的多样性:
标量返回值df[['School','Math','Height']].groupby('School').apply(lambda x:x.max())
列表返回值df[['School','Math','Height']].groupby('School').apply(lambda x:x-x.min()).head()
数据框返回值

df[['School','Math','Height']].groupby('School').apply(
lambda x:pd.DataFrame({'col1':x['Math']-x['Math'].max(),
                       'col2':x['Math']-x['Math'].min(),
                       'col3':x['Height']-x['Height'].max(),
                       'col4':x['Height']-x['Height'].min()})).head()

2. 用apply同时统计多个指标

此处可以借助OrderedDict工具进行快捷的统计:

from collections import OrderedDict
def f(df):
    data = OrderedDict()
    data['M_sum'] = df['Math'].sum()
    data['W_var'] = df['Weight'].var()
    data['H_mean'] = df['Height'].mean()
    return pd.Series(data)
print(grouped_single.apply(f))
         M_sum       W_var      H_mean
School                                
S_1      956.2  117.428571  175.733333
S_2     1191.1  181.081579  172.950000

问题与练习

【问题一】 什么是fillna的前向/后向填充,如何实现?

向前和向后填充,使用 ffill和 bfill
fillna(method='ffill')
fillna(method='bfill')

【问题二】 下面的代码实现了什么功能?请仿照设计一个它的groupby版本。

s = pd.Series ([0, 1, 1, 0, 1, 1, 1, 0])
s1 = s.cumsum()
result = s.mul(s1).diff().where(lambda x: x < 0).ffill().add(s1,fill_value =0)

print(result)打印出来是这样的

0    0.0
1    1.0
2    2.0
3    0.0
4    1.0
5    2.0
6    3.0
7    0.0
dtype: float64

【问题三】 如何计算组内0.25分位数与0.75分位数?要求显示在同一张表上。

def R1(x):
    return np.percentile(x,25)
def R2(x):
    return np.percentile(x,75)
print(grouped_single.agg(percentile_25=pd.NamedAgg(column='Math', aggfunc=R1),
                           percentile_75=pd.NamedAgg(column='Math', aggfunc=R2)))
        percentile_25  percentile_75
School                              
S_1             41.85         85.000
S_2             47.50         72.225

【问题四】 既然索引已经能够选出某些符合条件的子集,那么filter函数的设计有什么意义?

过滤是筛选组更大局,针对组的全体。

【问题五】 整合、变换、过滤三者在输入输出和功能上有何异同?

聚合主要还是对每个组,转换使用时是对组内每个对象;过滤是筛选组。

【问题六】 在带参数的多函数聚合时,有办法能够绕过wrap技巧实现同样功能吗?

在这里插入代码片
  1. 练习
    【练习一】: 现有一份关于diamonds的数据集Diamonds.csv,列分别记录了克拉数、颜色、开采深度、价格,请解决下列问题:
    (a) 在所有重量超过1克拉的钻石中,价格的极差是多少?
    (b) 若以开采深度的0.2\0.4\0.6\0.8分位数为分组依据,每一组中钻石颜色最多的是哪一种?该种颜色是组内平均而言单位重量最贵的吗?
    (c )以重量分组(0-0.5,0.5-1,1-1.5,1.5-2,2+),按递增的深度为索引排序,求每组中连续的严格递增价格序列长度的最大值。
    (d) 请按颜色分组,分别计算价格关于克拉数的回归系数。(单变量的简单线性回归,并只使用Pandas和Numpy完成)
df=pd.read_csv('work/Diamonds.csv')
print('(a) 在所有重量超过1克拉的钻石中,价格的极差是多少?')
print(df[df['carat']>1]['price'].agg(lambda x:x.max()-x.min()))
print('(b) 若以开采深度的0.2\0.4\0.6\0.8分位数为分组依据,每一组中钻石颜色最多的是哪一种?该种颜色是组内平均而言单位重量最贵的吗?')
bins = list(df['depth'].quantile([0,0.2,0.4,0.6,0.8,1]))
cuts = pd.cut(df['depth'],bins=bins) 
print(df.groupby(cuts)['color'].describe())
df['average_price']=df['price']/df['carat']
df['cuts'] = cuts
#df.groupby(['cuts','color'])['average_price'].mean()
#df.groupby(['cuts','color'])['average_price'].mean().groupby(['cuts']).idxmax().values
j = 0
for i in df.groupby(['cuts','color'])['average_price'].mean().groupby(['cuts']).idxmax().values:
    if i[1]==df.groupby(cuts)['color'].describe()['top'][j]:
        print(df.groupby(cuts)['color'].describe()['top'].index[j],sep=' ')
        print(i[1])
    j += 1
print('(c) 以重量分组(0-0.5,0.5-1,1-1.5,1.5-2,2+),按递增的深度为索引排序,求每组中连续的严格递增价格序列长度的最大值。')
cuts = pd.cut(df['carat'],bins=[0,0.5,1,1.5,2,np.inf]) 
df['cuts'] = cuts

def f(price_list):
    max_len = 0                            
    cur_len = 1                        
    for i in range(1, len(price_list)):      
        if price_list[i-1] < price_list[i]:        
            cur_len += 1                
            max_len = max(cur_len, max_len)     
        else:                       
            cur_len = 1                 
    return max_len

for name,group in df.groupby('cuts'):
    group = group.sort_values(by='depth')#按递增的深度为索引排序
    s = list(group['price'])
    print(name,f(s))
print('(d) 请按颜色分组,分别计算价格关于克拉数的回归系数。(单变量的简单线性回归,并只使用Pandas和Numpy完成)')
for i,j in df[['price','color','carat']].groupby('color'):
    X = list(j['carat'])
    Y = list(j['price'])
    b = np.polyfit(X, Y, 1)  #一次多项式拟合
    print(i,'颜色,回归系数',b[0])
(a) 在所有重量超过1克拉的钻石中,价格的极差是多少?
17561
(b) 若以开采深度的0.2.4.6.8分位数为分组依据,每一组中钻石颜色最多的是哪一种?该种颜色是组内平均而言单位重量最贵的吗?
              count unique top  freq
depth                               
(43.0, 60.8]  11294      7   E  2259
(60.8, 61.6]  11831      7   G  2593
(61.6, 62.1]  10403      7   G  2247
(62.1, 62.7]  10137      7   G  2193
(62.7, 79.0]  10273      7   G  2000
(62.1, 62.7]
G
(62.7, 79.0]
G
(c) 以重量分组(0-0.5,0.5-1,1-1.5,1.5-2,2+),按递增的深度为索引排序,求每组中连续的严格递增价格序列长度的最大值。
(0.0, 0.5] 8
(0.5, 1.0] 8
(1.0, 1.5] 7
(1.5, 2.0] 11
(2.0, inf] 7
(d) 请按颜色分组,分别计算价格关于克拉数的回归系数。(单变量的简单线性回归,并只使用Pandas和Numpy完成)
D 颜色,回归系数 8408.353125883317
E 颜色,回归系数 8296.212783456804
F 颜色,回归系数 8676.65834379266
G 颜色,回归系数 8525.345779322515
H 颜色,回归系数 7619.098319895736
I 颜色,回归系数 7761.041168809335
J 颜色,回归系数 7094.192092263141

【练习二】:有一份关于美国10年至17年的非法药物数据集Drugs.csv,列分别记录了年份、州(5个)、县、药物类型、报告数量,请解决下列问题:
(a) 按照年份统计,哪个县的报告数量最多?这个县所属的州在当年也是报告数最多的吗?
(b) 从14年到15年,Heroin的数量增加最多的是哪一个州?它在这个州是所有药物中增幅最大的吗?若不是,请找出符合该条件的药物。

df=pd.read_csv('work/Drugs.csv')
print('(a) 按照年份统计,哪个县的报告数量最多?这个县所属的州在当年也是报告数最多的吗?')
df=pd.read_csv('work/Drugs.csv')
year_dict1 = {}
for year,county in df.groupby(['YYYY','COUNTY'])['DrugReports'].sum().sort_values(ascending=False).index:
    if year not in year_dict1.keys():
        year_dict1[year] = county
year_dict2 = {}
for year,State in df.groupby(['YYYY','State'])['DrugReports'].sum().sort_values(ascending=False).index:
    if year not in year_dict2.keys():
        year_dict2[year] = State
dict0 = {}
for i in range(df.shape[0]):
    if df['COUNTY'][i] not in dict0.keys():
        dict0[df['COUNTY'][i]] = df['State'][i]
for i in sorted(year_dict1.keys()):
    State = year_dict1[i]
    if dict0[State]==year_dict2[i]:
        print(i,'年'+year_dict1[i]+'县的报告数量最多,这个县所属的州在当年也是报告数最多')
    else:
        print(i,'年'+year_dict1[i]+'县的报告数量最多,这个县所属的州在当年不是报告数最多,'+year_dict2[i]+'州在当年报告数最多')
print('(b) 从14年到15年,Heroin的数量增加最多的是哪一个州?它在这个州是所有药物中增幅最大的吗?若不是,请找出符合该条件的药物。')
print((df[(df['SubstanceName']=='Heroin')&(df['YYYY'].isin([2014,2015]))].groupby(['YYYY','State'])['DrugReports'].sum()[2015]-df[(df['SubstanceName']=='Heroin')&(df['YYYY'].isin([2014,2015]))].groupby(['YYYY','State'])['DrugReports'].sum()[2014]).idxmax()+'州从14年到15年Heroin的数量增加最多')
print(((df[(df['State']=='OH')&(df['YYYY'].isin([2014,2015]))].groupby(['YYYY','SubstanceName'])['DrugReports'].sum()[2015]-df[(df['State']=='OH')&(df['YYYY'].isin([2014,2015]))].groupby(['YYYY','SubstanceName'])['DrugReports'].sum()[2014])/df[(df['State']=='OH')&(df['YYYY'].isin([2014,2015]))].groupby(['YYYY','SubstanceName'])['DrugReports'].sum()[2014]).idxmax()+'是这个州所有药物中增幅最大的')
(a) 按照年份统计,哪个县的报告数量最多?这个县所属的州在当年也是报告数最多的吗?
2010 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年也是报告数最多
2011 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年不是报告数最多,OH州在当年报告数最多
2012 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年不是报告数最多,OH州在当年报告数最多
2013 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年不是报告数最多,OH州在当年报告数最多
2014 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年不是报告数最多,OH州在当年报告数最多
2015 年PHILADELPHIA县的报告数量最多,这个县所属的州在当年不是报告数最多,OH州在当年报告数最多
2016 年HAMILTON县的报告数量最多,这个县所属的州在当年也是报告数最多
2017 年HAMILTON县的报告数量最多,这个县所属的州在当年也是报告数最多
(b) 从14年到15年,Heroin的数量增加最多的是哪一个州?它在这个州是所有药物中增幅最大的吗?若不是,请找出符合该条件的药物。
OH州从14年到15年Heroin的数量增加最多
Acetyl fentanyl是这个州所有药物中增幅最大的

参考

  1. pandas官网
  2. Joyful-Pandas
  3. 用python的numpy作线性拟合、多项式拟合、对数拟合

你可能感兴趣的:(Pandas教程,python,数据分析)