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)

在这里插入图片描述

二、其他变形方法

  1. 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添加分隔符

在这里插入图片描述

  1. 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

reference

https://github.com/datawhalechina/joyful-pandas

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值