程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Pandas(三)—— 變形、連接

編輯:Python

Python模塊 —— Pandas

  • Pandas(三)—— 變形、連接
    • 五、變形
      • 5.1 長表變寬表 —— pivot / pivot_table
      • 5.2 寬表變長表 —— melt / wide_to_long
      • 5.3 交換行列索引 —— stack / unstack
      • 5.4 其他變形函數 —— crosstab / explode / get_dummies
      • 5.5 美國非法藥物數據集
    • 六、連接
      • 6.1 關系型連接 —— merge / join
      • 6.2 方向連接 —— concat / append / assign
      • 6.3 類連接操作 —— compare / combine
      • 6.4 美國疫情數據集

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. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved