Pandas基础4

第四章 变形

import numpy as np
import pandas as pd 
df = pd.read_csv('data/table.csv')
df.head()
SchoolClassIDGenderAddressHeightWeightMathPhysics
0S_1C_11101Mstreet_11736334.0A+
1S_1C_11102Fstreet_21927332.5B+
2S_1C_11103Mstreet_21868287.2B+
3S_1C_11104Fstreet_21678180.4B-
4S_1C_11105Fstreet_41596484.8B+

一、透视表

1. pivot

一般状态下,数据在DataFrame会以压缩(stacked)状态存放,例如上面的Gender,两个类别被叠在一列中,pivot函数可将某一列作为新的cols:

df.pivot(index='ID',columns='Class',values='Height').head()
ClassC_1C_2C_3C_4
ID
1101173.0NaNNaNNaN
1102192.0NaNNaNNaN
1103186.0NaNNaNNaN
1104167.0NaNNaNNaN
1105159.0NaNNaNNaN

然而pivot函数具有很强的局限性,除了功能上较少之外,还不允许values中出现重复的行列索引对(pair),例如下面的语句就会报错:

#df.pivot(index='School',columns='Gender',values='Height').head()

因此,更多的时候会选择使用强大的pivot_table函数

2. pivot_table

首先,再现上面的操作:

pd.pivot_table(df,index='ID',columns='Gender',values='Height').head()
GenderFM
ID
1101NaN173.0
1102192.0NaN
1103NaN186.0
1104167.0NaN
1105159.0NaN
%timeit df.pivot(index='ID',columns='Gender',values='Height')
%timeit pd.pivot_table(df,index='ID',columns='Gender',values='Height')
2.43 ms ± 164 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.8 ms ± 794 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

由于功能更多,速度上自然是比不上原来的pivot函数:

Pandas中提供了各种选项,下面介绍常用参数:

① aggfunc:对组内进行聚合统计,可传入各类函数,默认为’mean’

pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['max','sum']).head()
maxsum
GenderFMFM
School
S_119219513851251
S_219419319111548
pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum'],margins=True).head()
#margins_name可以设置名字,默认为'All'
meansum
GenderFMAllFMAll
School
S_1173.125000178.714286175.733333138512512636
S_2173.727273172.000000172.950000191115483459
All173.473684174.937500174.142857329627996095
pd.pivot_table(df,index=['School','Class'],
              columns=['Gender','Address'],
              values=['Height','Weight'],)
Height...Weight
GenderFM...FM
Addressstreet_1street_2street_4street_5street_6street_7street_1street_2street_4street_5...street_4street_5street_6street_7street_1street_2street_4street_5street_6street_7
SchoolClass
S_1C_1NaN179.5159.0NaNNaNNaN173.0186.0NaNNaN...64.0NaNNaNNaN63.082.0NaNNaNNaNNaN
C_2NaNNaN176.0162.0167.0NaNNaNNaNNaN188.0...94.063.063.0NaNNaNNaNNaN68.053.0NaN
C_3175.0NaNNaN187.0NaNNaNNaN195.0161.0NaN...NaN69.0NaNNaNNaN70.068.0NaNNaN82.0
S_2C_1NaNNaNNaN159.0161.0NaNNaNNaN163.5NaN...NaN97.061.0NaNNaNNaN71.0NaNNaN84.0
C_2NaNNaNNaNNaNNaN188.5175.0NaN155.0193.0...NaNNaNNaN76.574.0NaN91.0100.0NaNNaN
C_3NaNNaN157.0NaN164.0190.0NaNNaN187.0171.0...78.0NaN81.099.0NaNNaN73.088.0NaNNaN
C_4NaN176.0NaNNaN175.5NaNNaNNaNNaNNaN...NaNNaN57.0NaNNaNNaNNaNNaNNaN82.0

7 rows × 24 columns

3. crosstab(交叉表)

交叉表是一种特殊的透视表,典型的用途如分组统计,如现在想要统计关于街道和性别分组的频数:

pd.crosstab(index=df['Address'],columns=df['Gender'])
GenderFM
Address
street_112
street_242
street_435
street_533
street_651
street_733

交叉表的功能也很强大(但目前还不支持多级分组),下面说明一些重要参数:

① values和aggfunc:分组对某些数据进行聚合操作,这两个参数必须成对出现

pd.crosstab(index=df['Address'],columns=df['Gender'],
            values=np.random.randint(1,20,df.shape[0]),aggfunc='max')
#默认参数等于如下方法:
#pd.crosstab(index=df['Address'],columns=df['Gender'],values=1,aggfunc='count')
GenderFM
Address
street_147
street_21916
street_41919
street_51316
street_61513
street_71017
pd.crosstab(index=df['Address'],columns=df['Gender'],values=1,aggfunc='count')
GenderFM
Address
street_112
street_242
street_435
street_533
street_651
street_733

② 除了边际参数margins外,还引入了normalize参数,可选’all’,‘index’,'columns’参数值

pd.crosstab(index=df['Address'],columns=df['Gender'],normalize='all',margins=True)
GenderFMAll
Address
street_10.0285710.0571430.085714
street_20.1142860.0571430.171429
street_40.0857140.1428570.228571
street_50.0857140.0857140.171429
street_60.1428570.0285710.171429
street_70.0857140.0857140.171429
All0.5428570.4571431.000000

二、其他变形方法

1. melt

melt函数可以认为是pivot函数的逆操作,将unstacked状态的数据,压缩成stacked,使“宽”的DataFrame变“窄”

df_m = df[['ID','Gender','Math']]
df_m.head()
IDGenderMath
01101M34.0
11102F32.5
21103M87.2
31104F80.4
41105F84.8
df.pivot(index='ID',columns='Gender',values='Math').head()
GenderFM
ID
1101NaN34.0
110232.5NaN
1103NaN87.2
110480.4NaN
110584.8NaN

melt函数中的id_vars表示需要保留的列,value_vars表示需要stack的一组列

pivoted = df.pivot(index='ID',columns='Gender',values='Math')
result = pivoted.reset_index().melt(id_vars=['ID'],value_vars=['F','M'],value_name='Math')\
                     .dropna().set_index('ID').sort_index()
#检验是否与展开前的df相同,可以分别将这些链式方法的中间步骤展开,看看是什么结果
result.equals(df_m.set_index('ID'))
True
pivoted.reset_index().melt(id_vars=['ID'],value_vars=['F','M'],value_name='Math').dropna().set_index('ID').sort_index()
GenderMath
ID
1101M34.0
1102F32.5
1103M87.2
1104F80.4
1105F84.8
1201M97.0
1202F63.5
1203M58.8
1204F33.8
1205F68.4
1301M31.5
1302F87.7
1303M49.7
1304M85.2
1305F61.7
2101M83.3
2102F50.6
2103M52.5
2104F72.2
2105M34.2
2201M39.1
2202F68.5
2203M73.8
2204M47.2
2205F85.4
2301F72.3
2302M32.7
2303F65.9
2304F95.5
2305M48.9
2401F45.3
2402M48.7
2403F59.7
2404F67.7
2405F47.6

2. 压缩与展开

(1)stack:这是最基础的变形函数,总共只有两个参数:level和dropna

df_s = pd.pivot_table(df,index=['Class','ID'],columns='Gender',values=['Height','Weight'])
df_s.groupby('Class').head(2)
HeightWeight
GenderFMFM
ClassID
C_11101NaN173.0NaN63.0
1102192.0NaN73.0NaN
C_21201NaN188.0NaN68.0
1202176.0NaN94.0NaN
C_31301NaN161.0NaN68.0
1302175.0NaN57.0NaN
C_42401192.0NaN62.0NaN
2402NaN166.0NaN82.0
df_s.groupby('Class').head(2)
HeightWeight
GenderFMFM
ClassID
C_11101NaN173.0NaN63.0
1102192.0NaN73.0NaN
C_21201NaN188.0NaN68.0
1202176.0NaN94.0NaN
C_31301NaN161.0NaN68.0
1302175.0NaN57.0NaN
C_42401192.0NaN62.0NaN
2402NaN166.0NaN82.0

stack函数可以看做将横向的索引放到纵向,因此功能类似与melt,参数level可指定变化的列索引是哪一层(或哪几层,需要列表)

df_stacked = df_s.stack(0)
df_stacked.groupby('Class').head(2)
GenderFM
ClassID
C_11101HeightNaN173.0
WeightNaN63.0
C_21201HeightNaN188.0
WeightNaN68.0
C_31301HeightNaN161.0
WeightNaN68.0
C_42401Height192.0NaN
Weight62.0NaN

(2) unstack:stack的逆函数,功能上类似于pivot_table

df_stacked.head()
GenderFM
ClassID
C_11101HeightNaN173.0
WeightNaN63.0
1102Height192.0NaN
Weight73.0NaN
1103HeightNaN186.0
result = df_stacked.unstack().swaplevel(1,0,axis=1).sort_index(axis=1)
result.equals(df_s)
#同样在unstack中可以指定level参数
True
df_stacked.unstack().swaplevel(1,0,axis=1).sort_index(axis=1)
HeightWeight
GenderFMFM
ClassID
C_11101NaN173.0NaN63.0
1102192.0NaN73.0NaN
1103NaN186.0NaN82.0
1104167.0NaN81.0NaN
1105159.0NaN64.0NaN
2101NaN174.0NaN84.0
2102161.0NaN61.0NaN
2103NaN157.0NaN61.0
2104159.0NaN97.0NaN
2105NaN170.0NaN81.0
C_21201NaN188.0NaN68.0
1202176.0NaN94.0NaN
1203NaN160.0NaN53.0
1204162.0NaN63.0NaN
1205167.0NaN63.0NaN
2201NaN193.0NaN100.0
2202194.0NaN77.0NaN
2203NaN155.0NaN91.0
2204NaN175.0NaN74.0
2205183.0NaN76.0NaN
C_31301NaN161.0NaN68.0
1302175.0NaN57.0NaN
1303NaN188.0NaN82.0
1304NaN195.0NaN70.0
1305187.0NaN69.0NaN
2301157.0NaN78.0NaN
2302NaN171.0NaN88.0
2303190.0NaN99.0NaN
2304164.0NaN81.0NaN
2305NaN187.0NaN73.0
C_42401192.0NaN62.0NaN
2402NaN166.0NaN82.0
2403158.0NaN60.0NaN
2404160.0NaN84.0NaN
2405193.0NaN54.0NaN

三、哑变量与因子化

1. Dummy Variable(哑变量)

这里主要介绍get_dummies函数,其功能主要是进行one-hot编码:

df_d = df[['Class','Gender','Weight']]
df_d.head()
ClassGenderWeight
0C_1M63
1C_1F73
2C_1M82
3C_1F81
4C_1F64

现在希望将上面的表格前两列转化为哑变量,并加入第三列Weight数值:

pd.get_dummies(df_d[['Class','Gender']]).join(df_d['Weight']).head()
#可选prefix参数添加前缀,prefix_sep添加分隔符
Class_C_1Class_C_2Class_C_3Class_C_4Gender_FGender_MWeight
010000163
110001073
210000182
310001081
410001064

2. factorize方法

该方法主要用于自然数编码,并且缺失值会被记做-1,其中sort参数表示是否排序后赋值

codes, uniques = pd.factorize(['b', None, 'e', 'c', 'b'], sort=True)
display(codes)
display(uniques)
array([ 0, -1,  2,  1,  0], dtype=int64)



array(['b', 'c', 'e'], dtype=object)

四、问题与练习

1. 问题

【问题一】 上面提到了许多变形函数,如melt/crosstab/pivot/pivot_table/stack/unstack函数,请总结它们各自的使用特点。

【问题二】 变形函数和多级索引是什么关系?哪些变形函数会使得索引维数变化?具体如何变化?

【问题三】 请举出一个除了上文提过的关于哑变量方法的例子。

【问题四】 使用完stack后立即使用unstack一定能保证变化结果与原始表完全一致吗?

【问题五】 透视表中涉及了三个函数,请分别使用它们完成相同的目标(任务自定)并比较哪个速度最快。

【问题六】 既然melt起到了stack的功能,为什么再设计stack函数?

2. 练习

【练习一】

继续使用上一章的药物数据集:

pd.read_csv('data/Drugs.csv').head()
YYYYStateCOUNTYSubstanceNameDrugReports
02010VAACCOMACKPropoxyphene1
12010OHADAMSMorphine9
22010PAADAMSMethadone2
32010VAALEXANDRIA CITYHeroin5
42010PAALLEGHENYHydromorphone5

(a) 现在请你将数据表转化成如下形态,每行需要显示每种药物在每个地区的10年至17年的变化情况,且前三列需要排序:

(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)

【练习二】

现有一份关于某地区地震情况的数据集,请解决如下问题:

pd.read_csv('data/Earthquake.csv').head()
日期时间维度经度方向距离深度烈度
02003.05.2012:17:44 AM39.0440.38west0.110.00.0
12007.08.0112:03:08 AM40.7930.09west0.15.24.0
21978.05.0712:41:37 AM38.5827.61south_west0.10.00.0
31997.03.2212:31:45 AM39.4736.44south_west0.110.00.0
42000.04.0212:57:38 AM40.8030.24south_west0.17.00.0

(a) 现在请你将数据表转化成如下形态,将方向列展开,并将距离、深度和烈度三个属性压缩:

(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值