第五章 变形

参考:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch5.html#id2

一、长宽表的变形(元素和列索引的转换)

长表:把性别Gender存在列名中(long状态)

pd.DataFrame({'Gender':['F','F','M','M'], 'Height':[163, 160, 175, 180]})

Gender Height
0 F 163
1 F 160
2 M 175
3 M 180

宽表:如果把性别作为列名,列中的元素是某一其他的相关特征数值,那么这个表是关于性别的宽表(wide状态)

pd.DataFrame({'Height: F':[163, 160], 'Height: M':[175, 180]})

Height: F Height: M
0 163 175
1 160 180

变形函数:

分类 函数 例子 备注
长表转宽表 pivot df.pivot(index='Name', columns='Subject', values='Grade') 三个要素,分别是变形后的行索引、需要转到列索引的列,以及这些列和行索引对应的数值,它们分别对应了pivot方法中的index, columns, values参数。<br />且行列组合需要唯一
pivot_multi = df.pivot(index = ['Class', 'Name'],columns = ['Subject','Examination'],values = ['Grade','rank']) 参数可以传入列表,形成多级索引;仍然需要组合的唯一性
pivot_table df.pivot_table(index = 'Name',
columns = 'Subject',
values = 'Grade',
aggfunc = 'mean')
aggfunc 参数可以使用的聚合函数,不需要组合的唯一性,因为如果相同会被聚合起来
df.pivot_table(index = 'Name',
columns = 'Subject',
values = 'Grade',
aggfunc='mean',
margins=True)
pivot_table具有边际汇总的功能
宽表转长表 melt df_melted = df.melt(id_vars = ['Class', 'Name'],
value_vars = ['Chinese', 'Math'],
var_name = 'Subject',
value_name = 'Grade')
和pivot是互逆操作;只能合并一个元素
wide_to_long pd.wide_to_long(df,
stubnames=['Chinese', 'Math'],
i = ['Class', 'Name'],
j='Examination',
sep='_',
suffix='.+')
../_images/ch5_wtl.png
<br />可以合并一部分元素

【练一练】

在上面的边际汇总例子中,行或列的汇总为新表中行元素或者列元素的平均值,而总体的汇总为新表中四个元素的平均值。这种关系一定成立吗?若不成立,请给出一个例子来说明。

下面的代码会报错,size不能作为汇总函数

df.groupby(['Subject','Name']).size()
df.pivot_table(index = 'Name',
               columns = 'Subject',
               values = 'Grade',
               aggfunc='size',
               margins=True)

二、索引的变形(行列索引之间的交换)

函数 例子 备注
unstack df.unstack() 行索引转为列索引
df.unstack([0,2]) 主要参数是移动的层号,默认转化最内层,移动到列索引的最内层,同时支持同时转化多个层;必须保证 被转为列索引的行索引层 和 **被保留的行索引层 **构成的组合是唯一的,否则会报错
stack df.stack([1, 2]) 列索引的层压入行索引

三、其他变形函数

函数 例子 备注
crosstab pd.crosstab(index = df.School, columns = df.Transfer) 不是一个值得推荐使用的函数;在默认状态下,可以统计元素组合出现的频数,即count操作
pd.crosstab(index = df.School, columns = df.Transfer, values = [0]*df.shape[0], aggfunc = 'count') 也可以做其他的聚合操作,values传入要进行agg计算的类似序列的数据
explode df_ex = pd.DataFrame({'A': [[1, 2], 'my_str', {1, 2}, pd.Series([3, 4])],
'B': 1})
df_ex.explode('A')
对某一列的元素进行纵向的展开,被展开的单元格必须存储list, tuple, Series, np.ndarray中的一种类型。
get_dummies pd.get_dummies(df.Grade).head() 把类别特征转为指示变量;传入series或dataframe

练一练

前面提到了 crosstab 的性能劣于 pivot_table ,请选用多个聚合方法进行验证。

1分别对learn_pandas表进行pivot_table和crosstab的转换,计算count和mean:

import datetime
beg_time = datetime.datetime.now()
df.pivot_table(index = 'School',
               columns = 'Transfer',
               values = 'Name',
               aggfunc = 'count')
end_time = datetime.datetime.now()

print ("df.pivot_table:count", end_time - beg_time)
beg_time = datetime.datetime.now()
pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'count')
end_time = datetime.datetime.now()
print ("pd.crosstab:count", end_time - beg_time)

beg_time = datetime.datetime.now()
df.pivot_table(index = 'School',
               columns = 'Transfer',
               values = 'Name',
               aggfunc = 'sum')
end_time = datetime.datetime.now()

print ("df.pivot_table:sum", end_time - beg_time)
beg_time = datetime.datetime.now()
pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'sum')
end_time = datetime.datetime.now()
print ("pd.crosstab:sum", end_time - beg_time)

结果:似乎没有很慢?

'''
df.pivot_table:count 0:00:00.011967
pd.crosstab:count 0:00:00.009974
df.pivot_table:sum 0:00:00.013121
pd.crosstab:sum 0:00:00.010976
'''

2.是不是数据量不够大,换一个大点的表试试:(31569条数据house_info.xls)

import datetime
print("index数量",df2.groupby(['city','rooms']).ngroups)
beg_time = datetime.datetime.now()
df2 = pd.read_excel("../data/house_info.xls")
df2.pivot_table(index = 'city',
               columns = 'rooms',
               values = 'area',
               aggfunc = 'count')
end_time = datetime.datetime.now()

print ("df.pivot_table:count", end_time - beg_time)
beg_time = datetime.datetime.now()
pd.crosstab(index = df2.city, columns = df2.rooms, values = df2.area, aggfunc = 'count')
end_time = datetime.datetime.now()
print ("pd.crosstab:count", end_time - beg_time)

beg_time = datetime.datetime.now()
df2 = pd.read_excel("../data/house_info.xls")
df2['test'] = df2.index.values
df2.pivot_table(index = 'city',
               columns = 'rooms',
               values = 'test',
               aggfunc = 'mean')
end_time = datetime.datetime.now()

print ("df.pivot_table:mean", end_time - beg_time)
beg_time = datetime.datetime.now()
pd.crosstab(index = df2.city, columns = df2.rooms, values = df2.test, aggfunc = 'mean')
end_time = datetime.datetime.now()
print ("pd.crosstab:mean", end_time - beg_time)

结果:crosstab还是更快一点

index数量 4336
df.pivot_table:count 0:00:00.885630
pd.crosstab:count 0:00:00.017953
df.pivot_table:mean 0:00:00.568480
pd.crosstab:mean 0:00:00.013963        

3.换成3万条纯数字的,index数量为669:

import datetime
from string import ascii_uppercase
data = np.random.randint(0, 26, size=(3000, 10))
cols = list(ascii_uppercase[:10])
np.random.seed(2)
df3 = pd.DataFrame(data, columns=cols)
print("index数量", df3.groupby(['A', 'B'])['C'].ngroups)
beg_time = datetime.datetime.now()
df3.groupby(['A', 'B'])['C'].count().unstack(fill_value=0)
end_time = datetime.datetime.now()

print ("groupby:count", end_time - beg_time)
beg_time = datetime.datetime.now()
df3.pivot_table(values='C', index='A', columns='B', aggfunc='count', fill_value=0)
end_time = datetime.datetime.now()
print ("pivot_table:count", end_time - beg_time)


beg_time = datetime.datetime.now()
pd.crosstab(index=df3.A, columns=df3.B)
end_time = datetime.datetime.now()
print ("crosstab:count", end_time - beg_time)

结果:crosstab确实是最慢的

index数量 669
groupby:count 0:00:00.002988
pivot_table:count 0:00:00.011968
crosstab:count 0:00:00.015994

4.换成3万条纯字母的,index数量为676:

import datetime
from string import ascii_uppercase
data = np.random.choice(list(ascii_uppercase[:]), size=(30000, 10))
cols = list(ascii_uppercase[:10])
np.random.seed(2)

df3 = pd.DataFrame(data, columns=cols)
print("index数量",df3.groupby(['A', 'B'])['C'].ngroups)
beg_time = datetime.datetime.now()
df3.groupby(['A', 'B'])['C'].count().unstack(fill_value=0)
end_time = datetime.datetime.now()

print ("groupby:count", end_time - beg_time)
beg_time = datetime.datetime.now()
df3.pivot_table(values='C', index='A', columns='B', aggfunc='count', fill_value=0)
end_time = datetime.datetime.now()
print ("pivot_table:count", end_time - beg_time)


beg_time = datetime.datetime.now()
pd.crosstab(index=df3.A, columns=df3.B)
end_time = datetime.datetime.now()
print ("crosstab:count", end_time - beg_time)

结果:两者差不多

index数量 676
#crosstab慢于pivot_table
groupby:count 0:00:00.006970
pivot_table:count 0:00:00.013941
crosstab:count 0:00:00.016956
#测试多次的其他输出:crosstab快于pivot_table
index数量 676
groupby:count 0:00:00.016970
pivot_table:count 0:00:00.030903
crosstab:count 0:00:00.024946

结论:在index数量类似的情况下,crosstab处理字符串的速度和pivot_table差不多(甚至有的时候要快),而crosstab处理数字的速度始终比pivot_table慢,随着index数量的增长,差距越大

四、练习

Ex1:美国非法药物数据集

现有一份关于美国非法药物的数据集,其中 SubstanceName, DrugReports 分别指药物名称和报告数量:

In [63]: df = pd.read_csv('data/drugs.csv').sort_values([
   ....:      'State','COUNTY','SubstanceName'],ignore_index=True)
   ....: 

In [64]: df.head(3)
Out[64]: 
   YYYY State COUNTY  SubstanceName  DrugReports
0  2011    KY  ADAIR  Buprenorphine            3
1  2012    KY  ADAIR  Buprenorphine            5
2  2013    KY  ADAIR  Buprenorphine            4
  1. 将数据转为如下的形式:
../_images/Ex5_1.png
df2 = df.pivot_table(index=['State','COUNTY','SubstanceName'],columns=['YYYY'], values='DrugReports').reset_index().rename_axis(columns={'YYYY':''})
----------------------------
answer:
res = df.pivot(index=['State','COUNTY','SubstanceName'], columns='YYYY', values='DrugReports').reset_index().rename_axis(columns={'YYYY':''})
  1. 将第1问中的结果恢复为原表。

注意最后要排序

df2.melt(id_vars=['State','COUNTY','SubstanceName'],
         value_vars=[2010,2011,2012,2013,2014,2015,2016,2017],
         var_name='YYYY',
         value_name='DrugReports')
answer:
res_melted = res.melt(id_vars = ['State','COUNTY','SubstanceName'],
                      value_vars = res.columns[-8:],
                      var_name = 'YYYY',
                      value_name = 'DrugReports').dropna(
                      subset=['DrugReports'])
res_melted = res_melted[df.columns].sort_values(['State','COUNTY','SubstanceName'],ignore_index=True).astype({'YYYY':'int64', 'DrugReports':'int64'})
res_melted.equals(df)    
  1. State 分别统计每年的报告数量总和,其中 State, YYYY 分别为列索引和行索引,要求分别使用 pivot_table 函数与 groupby+unstack 两种不同的策略实现,并体会它们之间的联系。
df.pivot_table(values=['DrugReports'],index=['YYYY'],columns=['State'],aggfunc='sum')
df.groupby(['State','YYYY'])['DrugReports'].agg('sum').unstack([0])
answer:
res = df.groupby(['State', 'YYYY'])['DrugReports'].sum().to_frame().unstack(0).droplevel(0,axis=1)

Ex2:特殊的wide_to_long方法

从功能上看, melt 方法应当属于 wide_to_long 的一种特殊情况,即 stubnames 只有一类。请使用 wide_to_long 生成 melt 一节中的 df_melted 。(提示:对列名增加适当的前缀)

df = df.rename(columns={'Chinese':'Grade_Chinese','Math':'Grade_Math'})                   
pd.wide_to_long(df, 
                stubnames=['Grade'],
                i=['Class','Name'],
                j="Subject",
                sep='_',
                suffix='.+').reset_index().sort_values('Subject')
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 问1:将数据转为如下的形式: 问2:将第1问中的结果恢复为原表。 该问只做出了前面一部分,后面一部分没做出 后面部...
    wzz_1c19阅读 216评论 0 0
  • 1.长宽表的变形 什么是长表?什么是宽表?这个概念是对于某一个特征而言的。例如:一个表中把性别存储在某一个列中, ...
    58506fd3fbed阅读 252评论 0 0
  • 长宽表的变形 Pivot pivot 是一种典型的长表变宽表的函数对于一个基本的长变宽的操作而言,最重要的有三个要...
    罐罐儿111阅读 191评论 0 0
  • 写了个程序,对Pandas的绝大部分函数及其说明进行了中文翻译。原网址:http://pandas.pydata....
    TSIANG阅读 5,341评论 0 4
  • 一、数值计算和统计 1.基本参数axis轴和skipna跳过空值 df.mean() #.mean()默认列计算均...
    茶小美阅读 1,073评论 0 4