Pandas(三)—— 变形、连接

Pandas(三)—— 变形、连接

大家可以关注知乎或微信公众号的share16,我们也会同步更新此文章。

五、变形

   什么是长表?什么是宽表?这个概念是对于某一个特征而言的。例如:一个表中把性别存储在某一个列中,那么它就是关于性别的长表;如果把性别作为列名,列中的元素是某一其他的相关特征数值,那么这个表是关于性别的宽表。

   下面的两张表就分别是关于性别的长表和宽表:
在这里插入图片描述
在这里插入图片描述
   显然这两张表从信息上是完全等价的,它们包含相同的身高统计数值,只是这些数值的呈现方式不同,而其呈现方式主要又与性别一列选择的布局模式有关,即到底是以 long 的状态存储还是以 wide 的状态存储。因此,pandas 针对此类长宽表的变形操作设计了一些有关的变形函数。

5.1 长表变宽表 —— pivot / pivot_table

数据透视表(pivot / pivot_table):

df.pivot(index,columns,values) 等价于 pd.pivot(df,index,columns,values)

  • index/columns/values:列名,可用列表表示;
  • 利用 pivot 进行变形操作需要满足唯一性的要求,即在新表中的行列索引对应唯一的 value ;若不满足唯一性,可以使用pivot_table;
    在这里插入图片描述
    在这里插入图片描述

df.pivot_table(values,index,columns,aggfunc,fill_value,margins,dropna,margins_name,observed,sort) 等价于 pd.pivot_table(df,values,index,columns,aggfunc,fill_value,margins,dropna,margins_name,observed,sort)

  • index/columns/values:列名,可用列表表示;
  • aggfunc:函数,可用列表表示;默认为mean;
  • fill_value:默认None,用于替换缺失值的值;
  • margins:默认False,添加行/列的小计/总计;
  • dropna:默认True,是否删除全是缺失值的列;
  • margins_name:默认All,小计/总计所在行/列的名称;
  • observed:默认False;若为真,仅显示分类分组的观察值;反之显示分类分组的所有值;
  • sort:默认True,指是否对结果进行排序;
    在这里插入图片描述

5.2 宽表变长表 —— melt / wide_to_long

df.melt(id_vars,value_vars,var_name,value_name,col_level,ignore_index) 等价于 pd.melt(df,id_vars,value_vars,var_name,value_name,col_level,ignore_index)

  • id_vars:不需要被转换的列名,在转换后作为标识符列(不是索引列),可用元组、列表、数组表示;
  • value_vars:需要被转换的列名,若未指明,除id_vars之外的其他列都被转换,可用元组、列表、数组表示;
  • var_name:设置由 value_vars 组成的新的列名,若为None,则使用variable;
  • value_name:设置由 value_vars的数据 组成的新的列名,默认value;
  • col_level:(int或str)若列是多重索引,则使用此级别进行融合;
  • ignore_index :默认True,若为True,则忽略原始索引;若为 False,则保留原始索引;
    在这里插入图片描述

pd.wide_to_long(df,stubnames,i,j,sep,suffix)

  • stubnames:提取以指定字符串开头的列,并以指定字符串为新的列名,可用str、list表示;
  • i:用作索引的列,可用str、list表示;
  • j:新的一列命名,其数据用 含有stubnames的列名 的剩余部分填充;
  • sep:分隔符,含有stubnames的列名,用什么分割;
  • suffix:捕获正则表达式匹配的后缀,默认‘\d+’;
    在这里插入图片描述

5.3 交换行列索引 —— stack / unstack

   上篇文章中,我们学习到了索引内部的层交换,即 swaplevel 或 reorder_levels ;那么,行列索引之间的交换,可用 stack 或 unstack。

df.stack(level, dropna)

  • 将 列索引 转为 行索引
  • level:默认-1,可用int、str或列表;
  • dropna:是否删除缺失值,默认True;

df.unstack(level,fill_value)

  • 将 行索引 转为 列索引
  • level:默认-1,可用int、str或列表;
  • fill_value:若产生缺失值,则用此值替换NaN,默认None;
    在这里插入图片描述
    在这里插入图片描述

5.4 其他变形函数 —— crosstab / explode / get_dummies

pd.crosstab(index,columns,values,rownames,colnames,aggfunc,margins,margins_name,dropna,normalize)

  • 默认情况下,crosstab:用来计算因子的频率表
  • index/columns:行/列中进行分组的值,可用列表、数组或Series表示;
  • values:默认None,若有值,即根据因子对数据进行汇总,此时需要指定aggfunc;
  • rownames/colnames:默认None,若可以,需命名相应数量的名字;
  • margins:默认False,添加行/列的小计/总计;
  • margins_name:默认All,小计/总计所在行/列的名称;
  • dropna:默认True,是否删除全部为缺失值的列;

df.explode(column,ignore_index)

  • 对某一列的元素进行纵向的展开,被展开的单元格必须存储 list, tuple, Series, np.ndarray 中的一种类型
  • ignore_index:默认False,若为True,则生成的索引将标记为 0、1、…、n - 1;
df_ex = pd.DataFrame({'A': [[1, 2], 'my_str', {1, 2}, pd.Series([3, 4])], 'B': 1})
df_ex
df_ex.explode('A')
df_ex.explode('A', ignore_index=True)

pd.get_dummies(data,prefix,prefix_sep,dummy_na,columns,sparse,drop_first,dtype)

  • get_dummies 是用于特征构建的重要函数之一,其作用是把类别特征转为指示变量(属于返回1,反之返回0)
  • data:可为ndarray、Series、DataFrame
  • prefix:str,get_dummies转换后列名的前缀,默认None
  • dummy_na:默认False,若是 False ,忽略NaN,反之添加一列表示NaN;
  • columns:指定需要实现类别转换的列,否则转换所有列;
  • drop_first:默认False,是否通过删除第一级,从 k 个分类级别中取出 k-1 个虚拟变量;

5.5 美国非法药物数据集

现有一份关于美国非法药物的数据集点此下载,其中substancename、drugreports,分别代表:药物名称、报告数量;

问题:
1.  将数据转为下图的形式:
在这里插入图片描述

import pandas as pd
df = pd.read_csv('/Users/liuye/Desktop/Pandas数据集/05 美国非法药物.csv').sort_values(['year','state','county','substancename'],ignore_index=True)
a = df.pivot_table(values='drugreports', index=['state', 'county', 'substancename'], columns='year').reset_index().rename_axis(columns={'year':''})
df.pivot(index=['state', 'county', 'substancename'], columns='year', values='drugreports').reset_index().rename_axis(columns={'year':''})

# 解析:
# df.pivot_table(···)/df.pivot(···)的结果:行索引是state、county、substancename,列索引是year中的唯一值;
# reset_index():把索引变成普通列;  rename_axis():索引重命名

2.  将第1问中的结果恢复为原表;

b = a.melt(id_vars=['state', 'county', 'substancename'], var_name='year',value_name='drugreports').dropna(subset='drugreports')
c = b[df.columns].sort_values(['year','state','county','substancename'], ignore_index=True).astype({'year':'int64', 'drugreports':'int64'})
df.equals(c)

# 解析:
# b语句:将宽表转化成长表,删除drugreports列的缺失值
# c语句:将b表按df的列排序

3.  按state分别统计每年的报告数量总和,其中state和year分别为列索引和行索引,要求分别使用 pivot_table 函数与 groupby+unstack 两种不同的策略实现,并体会它们之间的联系;

df.pivot_table(values='drugreports', index='year', columns='state', aggfunc='sum')

df.groupby(['year','state']).drugreports.sum().unstack(1)

六、连接

6.1 关系型连接 —— merge / join

   把两张相关的表按照某一个或某一组键连接起来是一种常见操作,如:学生期末考试各个科目的成绩表按照姓名和班级连接成总的成绩表、对企业员工的各类信息表按照 员工ID号 进行连接汇总。由此可以看出,在关系型连接中,键是十分重要的,往往用on参数表示。另一个重要的要素是连接的形式。
   在 pandas 中的关系型连接函数 merge(值连接) 和 join(索引连接) 中提供了 how 参数来代表连接形式,分为左连接left、右连接right、内连接inner、外连接outer。

df1.merge(df2,how,on,left_on,right_on,left_index,right_index,sort,suffixes,copy, indicator,validate) 等价于 pd.merge(df1,df2,how,on,left_on,right_on,left_index,right_index,sort,suffixes,copy, indicator,validate)

  • how:默认inner(交集),类似于sql的join,还可取值left、right、outer(并集)、cross(笛卡尔积);
  • on/left_on/right_on:默认None,若df1与df2要连接的列名一样,用on;反之,用left_on和right_on;
  • left_index/right_index:默认False,用左边/右边的index作为连接键;若为多重索引,右侧/左侧的df中的连接键数必须与级别数相匹配;
  • sort:默认False,合并后的数据按字典顺序对连接键进行排序;
  • suffixes:默认(_x,_y),字符串组成的元组,当左右两表存在重复列名时,分别加上后缀用于区分;
  • indicator:默认False,若为True,结果数据新加一个名为‘_merge’的列,其返回值为:对于仅在左边匹配成功时,取值为left_only;仅在右边匹配成功时,则为right_only;两边都匹配成功时,则为both;
  • validate:默认None,若指定,则检查合并是否属于指定类型(1:1 —— 检查合并键在左右数据集中是否唯一值;1:m —— 检查合并键在左侧数据集中是否唯一值;m:1 —— 检查合并键在右侧数据集中是否唯一值);

df1.join(other,on,how,lsuffix,rsuffix,sort)

  • other:DataFrame、Series或DF列表; how:默认left;
  • lsuffix/rsuffix:左表/右表相同列索引的后缀;

6.2 方向连接 —— concat / append / assign

pd.concat(objs,axis,join,ignore_index,keys,levels,names,verify_integrity,sort,copy)

  • objs:要合并的DataFrame或Series,以列表传入;
  • axis:默认0,0 是行,1 是列;
  • join:默认outer(并集),还可取值inner(交集);
  • key:默认None,使用该序列构建层次化索引,且该索引值在最外层;

df.append(other,ignore_index,verify_integrity,sort)

  • 把other追加到df1的行末
  • other:DataFrame或Series对象,或这些对象的列表;
  • 若原表是int类型的索引,那可使用 ignore_index=True 对新序列对应的索引自动标号,否则必须对 Series 指定 name 属性;

df.assign(new_name,other,···)

  • 把other追加到df1的列末,并给其命名(new_name)
  • other:DataFrame或Series对象,或这些对象的列表;
  • 一般通过 df[new_name] = other 的形式,就可以等价地添加新列;然而,使用 [] 修改的缺点是它会直接在原表上进行修改,而 assign 返回的是一个临时副本;

6.3 类连接操作 —— compare / combine

df1.compare(df2,align_axis,keep_shape,keep_equal)

  • 比较df1与df2,并显示差异;返回的结果中,self 是指df1,other 是指df2;
  • keep_shape:默认False,若为真,则保留所有行和列;反之仅保留具有不同值的那些;
  • keep_equal:默认False,若为真,则相等的值保持原样;反之相等的值显示为NaN;

df1.combine(df2,func,fill_value,overwrite)

  • fill_value:默认None,用fill_value的值填充NaN;
  • overwrite:默认True,若为True,self中不存在于other中的列将被NaN覆盖;

6.4 美国疫情数据集

现有美国4月12日至11月16日的疫情报表点此下载,请将NewYork 的Confirmed、Deaths、Recovered、Active合并为一张表,索引为按如下方法生成的日期字符串序列;
pd.date_range(start,end,periods,freq,tz,normalize,name,closed,inclusive,···):返回一个固定频率的DatetimeIndex

  • start/end:生成日期的左/右边界;
  • periods:要生成的周期数,即序列中元素的个数;
  • freq:默认为D,还可是5H、3M等;
  • tz:默认情况下,生成的DatetimeIndex为timezone-naive,还可为‘Asia/Hong_Kong’等;
  • normalize:默认False,在生成日期范围之前将开始/结束日期标准化为午夜;
  • name:默认None,DatetimeIndex的名称;
  • inclusive:是否包含两个边界点(即start/end);默认both,取值还可为neither、left、right; closed:作用与inclusive类似;
''' 方法一 '''
import numpy as np
import pandas as pd

# 生成的日期字符串序列
a = [i.strftime('%m-%d-%Y')  for i in pd.date_range('20200412', '20201116')]
url0 = '/xxx/06 美国疫情/'

# 创建一个全是NaN的数据集
df_new = pd.DataFrame(np.full((1,4), np.nan), columns=['Confirmed','Deaths','Recovered','Active'])

# for循环读取csv文件
for i in a:
    url = url0 + i + '.csv'
    df = pd.read_csv(url)
    df_cut = df.query(" Province_State == 'New York' ")[['Confirmed','Deaths','Recovered','Active']]
    df_new = pd.concat([df_new,df_cut],ignore_index=True)

# 对最终文件进行修改
df_new = df_new.dropna()
df_new.index = a 
df_new


''' 方法二 '''
date = pd.date_range('20200412', '20201116').to_series()
date = date.dt.month.astype('string').str.zfill(2) +'-'+ date.dt.day.astype('string').str.zfill(2) +'-'+ '2020'
date = date.tolist()
L = []
for d in date:
    df = pd.read_csv('/Users/liuye/Desktop/data/06 美国疫情/' + d + '.csv', index_col='Province_State')
    data = df.loc['New York', ['Confirmed','Deaths','Recovered','Active']]
    L.append(data.to_frame().T)
res = pd.concat(L)
res.index = date
res

谢谢大家 🌹

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值