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指的是分组操作中的split-apply-combine过程
在该过程中,我们实际往往会遇到四类问题:
整合(Aggregation)——即分组计算统计量(如求均值、求每组元素个数)
变换(Transformation)——即分组对每个单元的数据进行操作(如元素标准化)
过滤(Filtration)——即按照某些规则筛选出一些组(如选出组内某一指标小于50的组)
综合问题——即前面提及的三种问题的混合
经过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,指明聚合的层级
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+
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对象可以使用相当多的函数,灵活程度很高
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对象的某个或者某几个列,上面的均分比较可以如下简洁地写出:
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
小结: 聚合主要还是对每个组,转换使用时是对组内每个对象
所谓聚合就是把一堆数,变成一个标量,而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])
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
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
NamedAgg不支持lambda函数,但是可以使用外置的def函数
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
判断是否组内数学分数至少有一个值在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()
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
这也是非常常用的!!!
transform函数中传入的对象是组内的列,并且返回值需要与列长完全一致
grouped_single[['Math','Height']].transform(lambda x:x-x.min()).head()
如果返回了标量值,那么组内的所有元素会被广播(就是大家都一样了)为这个值
grouped_single[['Math','Height']].transform(lambda x:x.mean()).head()
grouped_single[['Math','Height']].transform(lambda x:(x-x.mean())/x.std()).head()
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是应用最为广泛的(之前也有多次使用过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()
此处可以借助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技巧实现同样功能吗?
在这里插入代码片
Diamonds.csv
,列分别记录了克拉数、颜色、开采深度、价格,请解决下列问题: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是这个州所有药物中增幅最大的