joyful pandas第4章 变形
import numpy as np
import pandas as pd
df = pd.read_csv('table.csv')
df.head()
一、透视表
1. pivot
一般状态下,数据在DataFrame会以压缩(stacked)状态存放,例如上面的Gender,两个类别被叠在一列中,pivot函数可将某一列作为新的cols:
df.pivot(index='ID',columns='Gender',values='Height').head()
然而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()
由于功能更多,速度上自然是比不上原来的pivot函数:时间差10倍
%timeit df.pivot(index='ID',columns='Gender',values='Height')
%timeit pd.pivot_table(df,index='ID',columns='Gender',values='Height')
Pandas中提供了各种选项,下面介绍常用参数:
① aggfunc:对组内进行聚合统计,可传入各类函数,默认为’mean’
pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum']).head()
② margins:汇总边际状态
pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum'],margins=True).head()#margins_name可以设置名字,默认为'All'
③ 行、列、值都可以为多级
pd.pivot_table(df,index=['School','Class'], columns=['Gender','Address'], values=['Height','Weight'])
3. crosstab(交叉表)
交叉表是一种特殊的透视表,典型的用途如分组统计,如现在想要统计关于街道和性别分组的频数:
pd.crosstab(index=df['Address'],columns=df['Gender'])
交叉表的功能也很强大(但目前还不支持多级分组),下面说明一些重要参数:
① values和aggfunc:分组对某些数据进行聚合操作,这两个参数必须成对出现
pd.crosstab(index=df['Address'],columns=df['Gender'], values=np.random.randint(1,20,df.shape[0]),aggfunc='min')#默认参数等于如下方法:#pd.crosstab(index=df['Address'],columns=df['Gender'],values=1,aggfunc='count')
② 除了边际参数margins外,还引入了normalize参数,可选’all’,‘index’,'columns’参数值
pd.crosstab(index=df['Address'],columns=df['Gender'],normalize='all',margins=True)
二、其他变形方法
- melt
melt函数可以认为是pivot函数的逆操作,将unstacked状态的数据,压缩成stacked,使“宽”的DataFrame变“窄”
df_m = df[['ID','Gender','Math']]df_m.head()
df.pivot(index='ID',columns='Gender',values='Math').head()
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'))
output:True
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)
df_stacked = df_s.stack()
df_stacked.groupby('Class').head(2)
stack函数可以看做将横向的索引放到纵向,因此功能类似与melt,参数level可指定变化的列索引是哪一层(或哪几层,需要列表)
df_stacked = df_s.stack(0)
df_stacked.groupby('Class').head(2)
(2) unstack:stack的逆函数,功能上类似于pivot_table
df_stacked.head()
result = df_stacked.unstack().swaplevel(1,0,axis=1).sort_index(axis=1)
result.equals(df_s)#同样在unstack中可以指定level参数
Output: True
三、哑变量与因子化
1. Dummy Variable(哑变量)
这里主要介绍get_dummies函数,其功能主要是进行one-hot编码:
df_d = df[['Class','Gender','Weight']]df_d.head()
现在希望将上面的表格前两列转化为哑变量,并加入第三列Weight数值:
pd.get_dummies(df_d[['Class','Gender']]).join(df_d['Weight']).head()
#可选prefix参数添加前缀,prefix_sep添加分隔符
- factorize方法
该方法主要用于自然数编码,并且缺失值会被记做-1,其中sort参数表示是否排序后赋值
codes, uniques = pd.factorize(['b', None, 'a', 'c', 'b'], sort=True)
display(codes)
display(uniques)
四、问题与练习
1. 问题
【问题一】 上面提到了许多变形函数,如melt/crosstab/pivot/pivot_table/stack/unstack函数,请总结它们各自的使用特点。
melt函数可以认为是 pivot函数的逆操作,将value作为新的列,将原来的列合并为一个新的属性作为列
crosstab 是一种特殊的透视表,典型的用途如分组统计
**pivot:**将属性A设置为索引,将属性B的值域作为新的列,将属性C作为取值,要求对任意索引和列的组合,最多只能有一个属性C的值
def pivot_simple(index, columns, values):
"""
Produce 'pivot' table based on 3 columns of this DataFrame.
Uses unique values from index / columns and fills with values.
Parameters
----------
index : ndarray
Labels to use to make new frame's index
columns : ndarray
Labels to use to make new frame's columns
values : ndarray
Values to use for populating new frame's values
**pivot_table:**相对于privot,前面的操作一样,不过专门用于对任意索引和列的组合,可能有多个属性C的值的情况,pivot_table方法实现了类似pivot方法的功能,它可以在指定的列和行有重复的情况下使用,我们可以使用均值、中值或其他的聚合函数来计算重复条目中的单个值
def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean',
fill_value=None, margins=False, dropna=True,
margins_name='All'):
"""
Create a spreadsheet-style pivot table as a DataFrame. The levels in the
pivot table will be stored in MultiIndex objects (hierarchical indexes) on
the index and columns of the result DataFrame
Parameters
----------
data : DataFrame
values : column to aggregate, optional
index : column, Grouper, array, or list of the previous
If an array is passed, it must be the same length as the data. The list
can contain any of the other types (except list).
Keys to group by on the pivot table index. If an array is passed, it
is being used as the same manner as column values.
columns : column, Grouper, array, or list of the previous
If an array is passed, it must be the same length as the data. The list
can contain any of the other types (except list).
Keys to group by on the pivot table column. If an array is passed, it
is being used as the same manner as column values.
aggfunc : function or list of functions, default numpy.mean
If list of functions passed, the resulting pivot table will have
hierarchical columns whose top level are the function names (inferred
from the function objects themselves)
fill_value : scalar, default None
Value to replace missing values with
margins : boolean, default False
Add all row / columns (e.g. for subtotal / grand totals)
dropna : boolean, default True
Do not include columns whose entries are all NaN
margins_name : string, default 'All'
Name of the row / column that will contain the totals
when margins is True.
stack:将值域合并为属性,然后将属性划分到行,或者列
unstack:stack的逆操作——将行或列的属性的值域做为新的列
在用pandas进行数据重排时,经常用到stack和unstack两个函数。stack的意思是堆叠,堆积,unstack即“不要堆叠”,常见的数据的层次化结构有两种,一种是表格,一种是“花括号”,即下面这样的l两种形式:
表格在行列方向上均有索引(类似于DataFrame),花括号结构只有“列方向”上的索引(类似于层次化的Series),结构更加偏向于堆叠(Series-stack,方便记忆)。stack函数会将数据从”表格结构“变成”花括号结构“,即将其行索引变成列索引,反之,unstack函数将数据从”花括号结构“变成”表格结构“,即要将其中一层的列索引变成行索引。
reference: https://www.cnblogs.com/bambipai/p/7658311.html
【问题二】 变形函数和多级索引是什么关系?哪些变形函数会使得索引维数变化?具体如何变化?
可以通过设置变形函数的index,values参数来生成多级索引
【问题三】 请举出一个除了上文提过的关于哑变量方法的例子。
哑变量
也叫虚拟变量,引入哑变量的目的是,将不能够定量处理的变量量化,如职业、性别对收入的影响,战争、自然灾害对GDP的影响,
季节对某些产品(如冷饮)销售的影响等等。
这种“量化”通常是通过引入“哑变量”来完成的。根据这些因素的属性类型,构造只取“0”或“1”的人工变量,通常称为哑变量,记为D。
举一个例子,假设变量“职业”的取值分别为:工人、农民、学生、企业职员、其他,5种选项,我们可以增加4个哑变量来代替“职业”这个变量,
分别为D1(1=工人/0=非工人)、D2(1=农民/0=非农民)、D3(1=学生/0=非学生)、D4(1=企业职员/0=非企业职员),
最后一个选项“其他”的信息已经包含在这4个变量中了,所以不需要再增加一个D5(1=其他/0=非其他)了。
这个过程就是引入哑变量的过程,其实在结合分析(conjoint analysis)中,就是利用哑变量来分析各个属性的效用值的。
reference: https://blog.csdn.net/qq_38788128/article/details/80796776
【问题四】 使用完stack后立即使用unstack一定能保证变化结果与原始表完全一致吗?
df_s = pd.pivot_table(df,index=['Class','ID'],columns='Gender',values=['Height','Weight'])
df_s.stack().unstack().equals(df_stacked)
【问题五】 透视表中涉及了三个函数,请分别使用它们完成相同的目标(任务自定)并比较哪个速度最快。
%timeit df.pivot(index='ID',columns='Gender',values='Height')
%timeit pd.pivot_table(df,index='ID',columns='Gender',values = "Height")
%timeit pd.crosstab(index=df['ID'],columns=df['Gender'])
【问题六】 既然melt起到了stack的功能,为什么再设计stack函数?
stack函数的参数level可指定变化的列索引是哪一层
2. 练习
【练习一】 继续使用上一章的药物数据集:
pd.read_csv('Drugs.csv').head()
(a) 现在请你将数据表转化成如下形态,每行需要显示每种药物在每个地区的10年至17年的变化情况,且前三列需要排序:
df = pd.read_csv('Drugs.csv',index_col=['State','COUNTY']).sort_index()
df.head()
result = pd.pivot_table(df,index=['State','COUNTY','SubstanceName']
,columns='YYYY'
,values='DrugReports',fill_value='-').reset_index().rename_axis(columns={'YYYY':''})
result.head()
(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)
result_melted = result.melt(id_vars=result.columns[:3],value_vars=result.columns[-8:]
,var_name='YYYY',value_name='DrugReports').query('DrugReports != "-"')
result2 = result_melted.sort_values(by=['State','COUNTY','YYYY'
,'SubstanceName']).reset_index().drop(columns='index')
#下面其实无关紧要,只是交换两个列再改一下类型(因为‘-’所以type变成object了)
cols = list(result2.columns)
a, b = cols.index('SubstanceName'), cols.index('YYYY')
cols[b], cols[a] = cols[a], cols[b]
result2 = result2[cols].astype({'DrugReports':'int','YYYY':'int'})
result2.head()
df_tidy = df.reset_index().sort_values(by=result2.columns[:4].tolist()).reset_index().drop(columns='index')
df_tidy.head()
df_tidy.equals(result2) #True
【练习二】 现有一份关于某地区地震情况的数据集,请解决如下问题:
(a) 现在请你将数据表转化成如下形态,将方向列展开,并将距离、深度和烈度三个属性压缩:
df = pd.read_csv('Earthquake.csv')
df = df.sort_values(by=df.columns.tolist()[:3]).sort_index(axis=1).reset_index().drop(columns='index')
df.head()
result = pd.pivot_table(df,index=['日期','时间','维度','经度']
,columns='方向'
,values=['烈度','深度','距离'],fill_value='-').stack(level=0).rename_axis(index={None:'地震参数'})
result.head(6)
(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)
##bb
df_result = result.unstack().stack(0)[(~(result.unstack().stack(0)=='-')).any(1)].reset_index()
df_result.columns.name=None
df_result = df_result.sort_index(axis=1).astype({'深度':'float64','烈度':'float64','距离':'float64'})
df_result.head()
df_result.astype({'深度':'float64','烈度':'float64','距离':'float64'},copy=False).dtypes
df.equals(df_result) #True