學(xué)習(xí)所見的pandas操作總結(jié)

希望自己在兵荒馬亂中保持韌性,發(fā)現(xiàn)寶藏怠肋。


目錄

1.清除重復(fù)行或某個(gè)字段:drop_duplicates函數(shù)
2.apply函數(shù)
3.axis的理解
4.pd.read_csv()讀數(shù)據(jù)
5.stack()和unstack()函數(shù)
6.groupby函數(shù)的使用
7.數(shù)據(jù)索引
8.數(shù)據(jù)排序
9.計(jì)算某列有多少個(gè)不同的值,類似sql中count(distinct A)
10.數(shù)據(jù)篩選缎谷,類似sql中l(wèi)ike或where
11.將連續(xù)數(shù)據(jù)離散化操作或者類似sql中Case When
12.修改列名
13.map、apply灶似、applymap詳解
14.povit_table數(shù)據(jù)透視表的使用
15.數(shù)據(jù)表的合并和連接
16.全局替換dataframe.replace()
17.data.to_csv()

1.drop_duplicates

DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)
subset: 列名列林,可選,默認(rèn)為None
keep: {‘first’, ‘last’, False}, 默認(rèn)值 ‘first’
first: 保留第一次出現(xiàn)的重復(fù)行酪惭,刪除后面的重復(fù)行希痴。
last: 刪除重復(fù)項(xiàng),除了最后一次出現(xiàn)春感。
False: 刪除所有重復(fù)項(xiàng)砌创。
inplace:布爾值,默認(rèn)為False鲫懒,是否直接在原數(shù)據(jù)上刪除重復(fù)項(xiàng)或刪除重復(fù)項(xiàng)后返回副本嫩实。(inplace=True表示直接在原來的DataFrame上刪除重復(fù)項(xiàng),而默認(rèn)值False表示生成一個(gè)副本窥岩。)
根據(jù)數(shù)據(jù)的不同情況及處理數(shù)據(jù)的不同需求甲献,通常會(huì)分為兩種情況,一種是去除完全重復(fù)的行數(shù)據(jù)颂翼,另一種是去除某幾列重復(fù)的行數(shù)據(jù)晃洒,就這兩種情況可用下面的代碼進(jìn)行處理。

#去除完全重復(fù)的行數(shù)據(jù)
DataFrame.drop_duplicates()
#去除某幾列重復(fù)的行數(shù)據(jù)
DataFrame.drop_duplicates(subset=['A','B'],keep='first')
#示例朦乏,刪除positionId重復(fù)的行
data_duplicates=data.drop_duplicates(subset=['positionId'],keep='first')
data_duplicates.head()

2.apply函數(shù)

在pandas中球及,有Series和DataFrame兩者數(shù)據(jù)類型。針對(duì)這兩者類型使用apply函數(shù)有略微區(qū)別呻疹。

2.1 Series.apply
series = pd.Series([20, 21, 12], index=['London','New York','Helsinki'])
series
def square(x):
    x=x**2
    return x
series.apply(square)
series.apply(lambda x:x**2)
def subtract_custom_value(x, custom_value):
    return x-custom_value
series.apply(subtract_custom_value,custom_value=5)
def add_custom_values(x, **kwargs):
    for month in kwargs:
        x+=kwargs[month]
    return x
series.apply(add_custom_values,june=30, july=20, august=25)
series.apply(lambda x:np.log(x+1))
2.2 DataFrame.apply
df = pd.DataFrame([[4, 9,3],[3,5,2],[1,6,7]], columns=['A', 'B','C'])
df
df.apply(lambda x:x['A']+x['C'],axis=1)
df.apply(lambda x:np.sum(x),axis=0)

4.axis理解

axis是apply中的參數(shù)吃引,axis=1為橫向,axis=0為縱向刽锤,而不是行和列镊尺。這里axis=1就表示DataFrame根據(jù)lambda函數(shù)做橫向計(jì)算。

4.pd.read_csv

使用pd.read_csv()讀取數(shù)據(jù)姑蓝,對(duì)于數(shù)據(jù)路徑有三種辦法鹅心。一是ipynb文件和使用的數(shù)據(jù)集在同一個(gè)文件中,此時(shí)可以直接讀取纺荧。另一種就是ipynb文件和使用的數(shù)據(jù)集不在同一個(gè)文件中,此時(shí)讀取數(shù)據(jù)需要使用相對(duì)路徑和絕對(duì)路徑。

#ipynb文件和使用的數(shù)據(jù)集在同一個(gè)文件中
data=pd.read_csv('DataAnalyst.csv',encoding='gbk')
#ipynb文件和使用的數(shù)據(jù)集不在同一個(gè)文件中宙暇,使用相對(duì)路徑
data = pd.read_csv('C:/Users/Administrator/Desktop/秦路數(shù)據(jù)分析學(xué)習(xí)/第三周 Excel/DataAnalyst.csv',encoding='gbk')
#ipynb文件和使用的數(shù)據(jù)集不在同一個(gè)文件中输枯,使用絕對(duì)路徑
data = pd.read_csv('C:\\Users\\Administrator\\Desktop\\秦路數(shù)據(jù)分析學(xué)習(xí)\\第三周 Excel\\DataAnalyst.csv',encoding='gbk')

另外,pd.read_csv()的常用參數(shù):
header:指定行數(shù)用來作為列名占贫,數(shù)據(jù)開始行數(shù)桃熄。如果文件中沒有列名,則默認(rèn)為0型奥,否則設(shè)置為None瞳收。
sep:默認(rèn)的為',',即認(rèn)為文件是以逗號(hào)分隔(一般csv文件都是這個(gè)格式)厢汹,若是空格分隔螟深,可以使用"\s+"。
names:用于結(jié)果的列名列表烫葬,對(duì)各列重命名界弧,即添加表頭。
如數(shù)據(jù)有表頭搭综,但想用新的表頭垢箕,可以設(shè)置header=0,names=['a','b']實(shí)現(xiàn)表頭定制。
encoding:一般默認(rèn)為"utf-8"兑巾,如果文件中有中文条获,一般需要使用"gbk"既们。

5.stack()和unstack()函數(shù)

對(duì)數(shù)據(jù)做處理時(shí)洋魂,經(jīng)常需要對(duì)數(shù)據(jù)進(jìn)行reshape。其中脓鹃,stack和unstack是我們經(jīng)常用到的操作之一奋姿。
stack和unstack是python進(jìn)行層次化索引的重要操作锄开。層次化索引就是對(duì)索引進(jìn)行層次化分類,便于使用称诗,這里的索引可以是行索引萍悴,也可以是列索引。

常見的數(shù)據(jù)的層次化結(jié)構(gòu)有兩種寓免,一種是表格癣诱,一種是“花括號(hào)”,即下面這樣的兩種形式:
表格格式
花括號(hào)格式
表格在行列方向上均有索引袜香,花括號(hào)結(jié)構(gòu)只有“行方向”上的索引撕予。
其實(shí),應(yīng)用stack和unstack只需要記住下面的知識(shí)點(diǎn)即可:

stack: 將數(shù)據(jù)從”表格結(jié)構(gòu)“變成”花括號(hào)結(jié)構(gòu)“蜈首,即將其列索引變成行索引实抡。
unstack: 數(shù)據(jù)從”花括號(hào)結(jié)構(gòu)“變成”表格結(jié)構(gòu)“欠母,即要將其中一層的行索引變成列索引。如果是多層索引吆寨,則以上函數(shù)是針對(duì)內(nèi)層索引(這里是store)赏淌。利用level可以選擇具體哪層索引。

import pandas as pd
import numpy as np
from pandas import Series,DataFrame
data=DataFrame(np.arange(12).reshape((3,4)),index=pd.Index(['street1','street2','street3']),
               columns=pd.Index(['store1','store2','store3','store4']))
print('----------data--------')
print(data)
print('-------------data2----------------------------\n')
data2=data.stack()
data3=data2.unstack()
print(data2)
print('--------------data3---------------------------\n')
print(data3)

data4=data2.unstack(level=0)
print('-------data4----------')
print(data4)
data5=data2.unstack(level=-1) # 默認(rèn)的level=-1啄清,內(nèi)層的索引
print('------data5--------')
print(data5)
'''
----------data--------
         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
-------------data2----------------------------

street1  store1     0
         store2     1
         store3     2
         store4     3
street2  store1     4
         store2     5
         store3     6
         store4     7
street3  store1     8
         store2     9
         store3    10
         store4    11
dtype: int64
--------------data3---------------------------

         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
-------data4----------
        street1  street2  street3
store1        0        4        8
store2        1        5        9
store3        2        6       10
store4        3        7       11
------data5--------
         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
'''

可以看到:使用stack函數(shù)六水,將data的列索引['store1','store2','store3’,'store4']轉(zhuǎn)變成行索引(第二層),便得到了一個(gè)層次化的Series(data2)辣卒,使用unstack函數(shù)掷贾,將data2的第二層行索引轉(zhuǎn)變成列索引(默認(rèn)內(nèi)層索引,level=-1)荣茫,便又得到了DataFrame(data3)
下面的例子我們利用level選擇具體哪層索引想帅。

data4=data2.unstack(level=0)
print(data4)
'''
        street1  street2  street3
store1        0        4        8
store2        1        5        9
store3        2        6       10
store4        3        7       11
'''

我們可以清晰看到,當(dāng)我們?nèi)evel=0時(shí)计露,即最外層索引時(shí)博脑,unstack把行索引['street1','street2','street3’]變?yōu)榱肆兴饕?br> 重塑

6.groupby函數(shù)

#按照city和education分組
data_duplicates.groupby(['city','education']).mean()
#按city和education分組并計(jì)算bottomSalary最小值,最大值和平均值
data_duplicates.groupby(['city','education'])['bottomSalary'].agg(['min','max','mean'])
data_duplicates.groupby(['city','education']).agg({'bottomSalary':['mean','min'],'topSalary':'max','salary':'count'})

在日常的數(shù)據(jù)分析中票罐,經(jīng)常需要將數(shù)據(jù)根據(jù)某個(gè)(多個(gè))字段劃分為不同的群體(group)進(jìn)行分析叉趣,如電商領(lǐng)域?qū)⑷珖目備N售額根據(jù)省份進(jìn)行劃分,分析各省銷售額的變化情況该押,社交領(lǐng)域?qū)⒂脩舾鶕?jù)畫像(性別疗杉、年齡)進(jìn)行細(xì)分,研究用戶的使用情況和偏好等蚕礼。在Pandas中烟具,上述的數(shù)據(jù)處理操作主要運(yùn)用groupby完成,在這里著重介紹一下groupby的基本原理及對(duì)應(yīng)的agg奠蹬、transform和apply操作朝聋。
為了后續(xù)圖解的方便,采用模擬生成的10個(gè)樣本數(shù)據(jù)囤躁,代碼和數(shù)據(jù)如下:

company=["A","B","C"]
business=['X','Y','Z']
data=pd.DataFrame({
    "company":[company[x] for x in np.random.randint(0,len(company),10)],
    "business":[business[x] for x in np.random.randint(0,len(business),10)],
    "salary":np.random.randint(5,50,10),
    "age":np.random.randint(15,50,10)
}
)
data
1.Groupby的基本原理

在pandas中冀痕,實(shí)現(xiàn)分組操作的代碼很簡單,僅需一行代碼狸演,在這里言蛇,將上面的數(shù)據(jù)集按照company字段進(jìn)行劃分:

company_group=data.groupby(['company'])
company_group

將上述代碼輸入ipython后,會(huì)得到一個(gè)DataFrameGroupBy對(duì)象

那這個(gè)生成的DataFrameGroupBy是啥呢宵距?對(duì)data進(jìn)行了groupby后發(fā)生了什么腊尚?ipython所返回的結(jié)果是其內(nèi)存地址,并不利于直觀地理解满哪,為了看看group內(nèi)部究竟是什么婿斥,這里把group轉(zhuǎn)換成list的形式來看一看:

list(company_group)
[('A',   company business  salary  age
  0       A        Z      37   21
  1       A        Y       5   44
  3       A        X      38   30
  4       A        Y      31   36
  9       A        X      28   17), 
 ('B',   company business  salary  age
  7       B        Y      15   37
  8       B        Y      15   18), 
 ('C',   company business  salary  age
  2       C        X      40   17
  5       C        Y      15   30
  6       C        Y      19   26)]

轉(zhuǎn)換成列表的形式后劝篷,可以看到,列表由三個(gè)元組組成受扳,每個(gè)元組中携龟,第一個(gè)元素是組別(這里是按照company進(jìn)行分組兔跌,所以最后分為了A,B,C)勘高,第二個(gè)元素的是對(duì)應(yīng)組別下的DataFrame,整個(gè)過程可以圖解如下:

總結(jié)來說坟桅,groupby的過程就是將原有的DataFrame按照groupby的字段(這里是company)华望,劃分為若干個(gè)分組DataFrame,被分為多少個(gè)組就有多少個(gè)分組DataFrame仅乓。所以說赖舟,在groupby之后的一系列操作(如agg、apply等)夸楣,均是基于子DataFrame的操作宾抓。理解了這點(diǎn),也就基本摸清了Pandas中g(shù)roupby操作的主要原理豫喧。下面來講講groupby之后的常見操作石洗。

2.agg聚合操作

聚合操作是groupby后非常常見的操作,會(huì)寫SQL的朋友對(duì)此應(yīng)該是非常熟悉了紧显。聚合操作可以用來求和讲衫、均值、最大值孵班、最小值等涉兽,下面的表格列出了Pandas中常見的聚合操作。

針對(duì)樣例數(shù)據(jù)集篙程,如果我想求不同公司員工的平均年齡和平均薪水枷畏,可以按照下方的代碼進(jìn)行:

data.groupby(['company']).mean()

如果想對(duì)針對(duì)不同的列求不同的值,比如要計(jì)算不同公司員工的平均年齡以及薪水的中位數(shù)虱饿,可以利用字典進(jìn)行聚合操作的指定:

data.groupby(['company']).agg({'salary':'median','age':'mean'})
3.transform

transform是一種什么數(shù)據(jù)操作拥诡?和agg有什么區(qū)別呢?為了更好地理解transform和agg的不同郭厌,下面從實(shí)際的應(yīng)用場景出發(fā)進(jìn)行對(duì)比袋倔。
在上面的agg中,我們學(xué)會(huì)了如何求不同公司員工的平均薪水折柠,如果現(xiàn)在需要在原數(shù)據(jù)集中新增一列avg_salary宾娜,代表員工所在的公司的平均薪水(相同公司的員工具有一樣的平均薪水),該怎么實(shí)現(xiàn)呢扇售?如果按照正常的步驟來計(jì)算前塔,需要先求得不同公司的平均薪水嚣艇,然后按照員工和公司的對(duì)應(yīng)關(guān)系填充到對(duì)應(yīng)的位置,不用transform的話华弓,實(shí)現(xiàn)代碼如下:

avg_salary=data.groupby(['company']).salary.mean().to_dict()
avg_salary
data['avgsalary'] = data['company'].map(avg_salary)
data

如果使用transform的話食零,僅需要一行代碼:

data['avg_salary']=data.groupby(['company']).salary.transform('mean')
data

還是以圖解的方式來看看進(jìn)行g(shù)roupby后transform的實(shí)現(xiàn)過程(為了更直觀展示,圖中加入了company列寂屏,實(shí)際按照上面的代碼只有salary列):

圖中的大方框是transform和agg所不一樣的地方贰谣,對(duì)agg而言,會(huì)計(jì)算得到A迁霎,B吱抚,C公司對(duì)應(yīng)的均值并直接返回,但對(duì)transform而言考廉,則會(huì)對(duì)每一條數(shù)據(jù)求得相應(yīng)的結(jié)果秘豹,同一組內(nèi)的樣本會(huì)有相同的值,組內(nèi)求完均值后會(huì)按照原索引的順序返回結(jié)果昌粤,如果有不理解的可以拿這張圖和agg那張對(duì)比一下既绕。

4.apply

apply相比agg和transform而言更加靈活,能夠傳入任意自定義的函數(shù)涮坐,實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)操作凄贩。但apply在groupby后使用apply有什么特別的呢?
對(duì)于groupby后的apply膊升,以分組后的子DataFrame作為參數(shù)傳入指定函數(shù)的怎炊,基本操作單位是DataFrame,而一般apply的基本操作單位是Series廓译。還是以一個(gè)案例來介紹groupby后的apply用法评肆。
假設(shè)我現(xiàn)在需要獲取各個(gè)公司年齡最大的員工的數(shù)據(jù),該怎么實(shí)現(xiàn)呢非区?可以用以下代碼實(shí)現(xiàn):

def get_oldest_staff(x):
    df = x.sort_values(by='age',ascending=False)
    return df.iloc[0,:]
data.groupby('company',as_index=False).apply(get_oldest_staff)

這樣便得到了每個(gè)公司年齡最大的員工的數(shù)據(jù)瓜挽,整個(gè)流程圖解如下:
可以看到,此處的apply和上篇文章中所介紹的作用原理基本一致征绸,只是傳入函數(shù)的參數(shù)由Series變?yōu)榱舜颂幍姆纸MDataFrame久橙。
最后,關(guān)于apply的使用管怠,這里有個(gè)小建議淆衷,雖然說apply擁有更大的靈活性,但apply的運(yùn)行效率會(huì)比agg和transform更慢渤弛。所以祝拯,groupby之后能用agg和transform解決的問題還是優(yōu)先使用這兩個(gè)方法,實(shí)在解決不了了才考慮使用apply進(jìn)行操作。
知乎--Groupby函數(shù)

7.數(shù)據(jù)索引

#單獨(dú)取出某一列
data_duplicates['city']
#取出多列
data_duplicates[['city','education']]
# 取出某列的某一行
data_duplicates['city'][0]
# 取出某列的某幾行
data_duplicates['city'][:10]
#取出某幾列的某幾行
data_duplicates[['city','education','salary']][:1]

# loc方法索引
'''
DataFrame.loc[行名,列名]
'''
# 取出某幾列的某幾行
data_duplicates.loc[[0,1,5],['city','education','salary']]
# 取出 city ,education ,salary的0-20行所有索引名稱為偶數(shù)的數(shù)據(jù)
data_duplicates.loc[0:21:2,['city','education','salary']]

#iloc方法索引
'''
DataFrame.iloc[行位置佳头,列位置]
'''
#取出某幾列的某幾行
data_duplicates.iloc[[0,1,5],[0,1,2]]
# 取出前五列的奇數(shù)列的0-20行所有索引名稱為偶數(shù)的數(shù)據(jù)
data_duplicates.iloc[0:21:2,0:5:2]

#ix方法索引
'''
DataFrame.ix[行位置/行名,列位置/列名]
'''
#篩選出A條件或B條件的樣本鹰贵。這里是組為實(shí)驗(yàn)組,頁面為新頁面或者組為控制組康嘉,頁面為舊頁面的樣本
df1=df[((df['group']=='treatment')&(df['landing_page']=='new_page'))|((df['group']=='control')&(df['landing_page']=='old_page'))]

數(shù)據(jù)索引參考文章

8.數(shù)據(jù)排序

#Series.sort_values(ascending=False)False降序碉输,True升序
data_duplicates['bottomSalary'].sort_values(ascending=False)
#df.sort_values(by=['A','B'],ascending=False)按by的字段進(jìn)行排序
data_duplicates.sort_values(by='bottomSalary',ascending=False)
data_duplicates.sort_values(by=['bottomSalary','topSalary'],ascending=False)

9.計(jì)算某列有多少個(gè)不同的值,類似sql中count(distinct A)

data_duplicates['education'].nunique()

10.數(shù)據(jù)篩選,類似sql中l(wèi)ike或where

#篩選教育背景要求本科及以上
data_duplicates[data_duplicates['education'].isin(['本科','碩士','博士'])]
#篩選受教育背景非大專的樣本
data_duplicates[data_duplicates['education']!='大專']
#篩選公司名字中帶直播的數(shù)據(jù)
data_duplicates[data_duplicates['companyShortName'].str.contains('直播')]
#篩選公司名字中帶直播或者騰訊的數(shù)據(jù)
data_duplicates[data_duplicates['companyShortName'].str.contains('直播|騰訊')]
#多個(gè)條件篩選--在北京月薪10k起的直播行業(yè)招聘
data_duplicates[(data_duplicates['city']=='北京')&(data_duplicates['companyShortName'].str.contains('直播'))&(data_duplicates['bottomSalary']>10)]

11.將連續(xù)數(shù)據(jù)離散化操作或者類似sql中Case When

#根據(jù)最低薪資水平創(chuàng)建新的列SalaryType
#方法一
data_duplicates.loc[data_duplicates['bottomSalary']<=10,'SalaryType']='低薪資'
data_duplicates.loc[(data_duplicates['bottomSalary']>10)&(data_duplicates['bottomSalary']<=30),'SalaryType']='中等薪資'
data_duplicates.loc[data_duplicates['bottomSalary']>30,'SalaryType']='高薪資'
#方法二 pd.cut() 左開右閉 (0,10],(10,20],(20,1000]
data_duplicates['SalaryType1'] = pd.cut(data_duplicates['bottomSalary'],[0,10,20,1000],labels=['低薪資','中等薪資','高薪資'])

12.修改列名

data_duplicates.rename(columns={'city':'城市','公司名稱':'companyname'},inplace=True)

13.map亭珍、apply敷钾、applymap詳解

pandas數(shù)據(jù)處理的三板斧

在日常的數(shù)據(jù)處理中,經(jīng)常會(huì)對(duì)一個(gè)DataFrame進(jìn)行逐行块蚌、逐列和逐元素的操作闰非,對(duì)應(yīng)這些操作膘格,Pandas中的map峭范、apply和applymap可以解決絕大部分這樣的數(shù)據(jù)處理需求。

boolean=[True,False]
gender=["男","女"]
color=["white","black","yellow"]
data=pd.DataFrame({
    "height":np.random.randint(150,190,100),
    "weight":np.random.randint(40,90,100),
    "smoker":[boolean[x] for x in np.random.randint(0,2,100)],
    "gender":[gender[x] for x in np.random.randint(0,2,100)],
    "age":np.random.randint(15,90,100),
    "color":[color[x] for x in np.random.randint(0,len(color),100) ]
})
data.head()
Series處理
1.map用法

如果需要把數(shù)據(jù)集中g(shù)ender列的男替換為1瘪贱,女替換為0纱控,怎么做呢?絕對(duì)不是用for循環(huán)實(shí)現(xiàn)菜秦,使用Series.map()可以很容易做到甜害,最少僅需一行代碼。

#第一種方法
data['gender'] = data['gender'].map({'男':1,'女':0})
#第二種方法
#使用函數(shù)
def gender_map(x):  #這里x表示的是Series的一個(gè)元素球昨,對(duì)Series元素依次處理
    if x=='男':
        gender=1
    else:
        gender=0
    return gender
data['gender']=data['gender'].map(gender_map)

那map在實(shí)際過程中是怎么運(yùn)行的呢尔店?請(qǐng)看下面的圖解(為了方便展示,僅截取了前10條數(shù)據(jù))

不論是利用字典還是函數(shù)進(jìn)行映射主慰,map方法都是把對(duì)應(yīng)的數(shù)據(jù)逐個(gè)當(dāng)作參數(shù)傳入到字典或函數(shù)中嚣州,得到映射后的值。

2.apply用法

同時(shí)Series對(duì)象還有apply方法共螺,apply方法的作用原理和map方法類似该肴,區(qū)別在于apply能夠傳入功能更為復(fù)雜的函數(shù)。怎么理解呢藐不?一起看看下面的例子匀哄。

假設(shè)在數(shù)據(jù)統(tǒng)計(jì)的過程中,年齡age列有較大誤差雏蛮,需要對(duì)其進(jìn)行調(diào)整(加上或減去一個(gè)值)涎嚼,由于這個(gè)加上或減去的值未知,故在定義函數(shù)時(shí)挑秉,需要加多一個(gè)參數(shù)bias法梯,此時(shí)用map方法是操作不了的(傳入map的函數(shù)只能接收一個(gè)參數(shù)),apply方法則可以解決這個(gè)問題衷模。

def apply_age(x,**kwargs):
    for i in kwargs:
        x+=kwargs[i]
    return x
#以字典的形式傳遞參數(shù)
data['age'] = data['age'].apply(apply_age,list_1=6,list_2=3,list_3=1)
data.head()

總而言之鹊汛,對(duì)于Series而言蒲赂,map可以解決絕大多數(shù)的數(shù)據(jù)處理需求,但如果需要使用較為復(fù)雜的函數(shù)刁憋,則需要用到apply方法滥嘴。

DataFrame處理

對(duì)于DataFrame而言,基本沒有DataFrame.map()的使用至耻,map方法都是針對(duì)Series的處理若皱。

1.apply

對(duì)DataFrame而言,apply是非常重要的數(shù)據(jù)處理方法尘颓,它可以接收各種各樣的函數(shù)(Python內(nèi)置的或自定義的)走触,處理方式很靈活,下面通過幾個(gè)例子來看看apply的具體使用及其原理疤苹。

在進(jìn)行具體介紹之前互广,首先需要介紹一下DataFrame中axis的概念,在DataFrame對(duì)象的大多數(shù)方法中卧土,都會(huì)有axis這個(gè)參數(shù)惫皱,它控制了你指定的操作是沿著0軸還是1軸進(jìn)行。axis=0代表操作對(duì)列columns進(jìn)行尤莺,axis=1代表操作對(duì)行row進(jìn)行旅敷,如下圖所示。
如果還不是很了解颤霎,沒關(guān)系媳谁,下面會(huì)分別對(duì)apply沿著0軸以及1軸的操作進(jìn)行講解,繼續(xù)往下走友酱。

假設(shè)現(xiàn)在需要對(duì)data中的數(shù)值列分別進(jìn)行取對(duì)數(shù)和求和的操作晴音,這時(shí)可以用apply進(jìn)行相應(yīng)的操作,因?yàn)槭菍?duì)列進(jìn)行操作粹污,所以需要指定axis=0段多,使用下面的兩行代碼可以很輕松地解決我們的問題。

#沿著0軸求和
data[['height','weight','age']].apply(lambda x:sum(x),axis=0)
#沿著0軸求log
data[['height','weight','age']]=data[['height','weight','age']].apply(lambda x:np.log(x),axis=0)
data.head()

實(shí)現(xiàn)的方式很簡單壮吩,但調(diào)用apply時(shí)究竟發(fā)生了什么呢进苍?過程是怎么實(shí)現(xiàn)的?還是通過圖解的方式來一探究竟鸭叙。(取前五條數(shù)據(jù)為例)

當(dāng)沿著軸0(axis=0)進(jìn)行操作時(shí)觉啊,會(huì)將各列(columns)默認(rèn)以Series的形式作為參數(shù),傳入到你指定的操作函數(shù)中沈贝,操作后合并并返回相應(yīng)的結(jié)果杠人。
那如果在實(shí)際使用中需要按行進(jìn)行操作(axis=1),那整個(gè)過程又是怎么實(shí)現(xiàn)的呢?
在數(shù)據(jù)集中,有身高和體重的數(shù)據(jù)嗡善,所以根據(jù)這個(gè)辑莫,我們可以計(jì)算每個(gè)人的BMI指數(shù)(體檢時(shí)常用的指標(biāo),衡量人體肥胖程度和是否健康的重要標(biāo)準(zhǔn))罩引,計(jì)算公式是:體重指數(shù)BMI=體重/身高的平方(國際單位kg/㎡),因?yàn)樾枰獙?duì)每個(gè)樣本進(jìn)行操作袁铐,這里使用axis=1的apply進(jìn)行操作揭蜒,代碼如下:

def BMI(x):
    weight=x['weight']
    height=x['height']/100
    BMI = weight/height**2
    return BMI
data['BMI'] = data.apply(BMI,axis=1)
data.head()

當(dāng)apply設(shè)置了axis=1對(duì)行進(jìn)行操作時(shí),會(huì)默認(rèn)將每一行數(shù)據(jù)以Series的形式(Series的索引為列名)傳入指定函數(shù)剔桨,返回相應(yīng)的結(jié)果屉更。
總結(jié)一下對(duì)DataFrame的apply操作:
1.當(dāng)axis=0時(shí),對(duì)每列columns執(zhí)行指定函數(shù)洒缀;當(dāng)axis=1時(shí)瑰谜,對(duì)每行row執(zhí)行指定函數(shù)。
2.無論axis=0還是axis=1帝洪,其傳入指定函數(shù)的默認(rèn)形式均為Series似舵,可以通過設(shè)置raw=True傳入numpy數(shù)組。
3.對(duì)每個(gè)Series執(zhí)行結(jié)果后葱峡,會(huì)將結(jié)果整合在一起返回(若想有返回值,定義函數(shù)時(shí)需要return相應(yīng)的值)
4.當(dāng)然龙助,DataFrame的apply和Series的apply一樣砰奕,也能接收更復(fù)雜的函數(shù),如傳入?yún)?shù)等提鸟,實(shí)現(xiàn)原理是一樣的军援,具體用法詳見官方文檔。

2.applymap

applymap的用法比較簡單称勋,會(huì)對(duì)DataFrame中的每個(gè)單元格執(zhí)行指定函數(shù)的操作胸哥,雖然用途不如apply廣泛,但在某些場合下還是比較有用的赡鲜,如下面這個(gè)例子空厌。

df = pd.DataFrame(
    {
        "A":np.random.randn(5),
        "B":np.random.randn(5),
        "C":np.random.randn(5),
        "D":np.random.randn(5),
        "E":np.random.randn(5),
    }
)
df

現(xiàn)在想將DataFrame中所有的值保留兩位小數(shù)顯示,使用applymap可以很快達(dá)到你想要的目的银酬,代碼和圖解如下:

df.applymap(lambda x:'%.2f'%x)

14.pivot_table數(shù)據(jù)透視表

從功能上講嘲更,Pandas 中用透視表 (pivot table) 和 Excel 里面的透視表是一樣的。


pivot_table參數(shù)的含義

Pivot 字面意思是支點(diǎn)揩瞪,即上圖中的 index 和 columns 指定的行和列標(biāo)簽赋朦,支點(diǎn)可想理解成數(shù)據(jù) (values) 在哪個(gè)維度上做整合 (aggfunc),再吧 NaN 值用 fill_value 替代,按行按列加總 (margin=True)宠哄。

boolean=[True,False]
gender=["男","女"]
color=["white","black","yellow"]
data=pd.DataFrame({
    "height":np.random.randint(150,190,100),
    "weight":np.random.randint(40,90,100),
    "smoker":[boolean[x] for x in np.random.randint(0,2,100)],
    "gender":[gender[x] for x in np.random.randint(0,2,100)],
    "age":np.random.randint(15,90,100),
    "color":[color[x] for x in np.random.randint(0,len(color),100) ]
})
data.head()
設(shè)置"單行"為pivot

創(chuàng)建透視表的 pivot_table() 函數(shù)里面的參數(shù)設(shè)置很多壹将,學(xué)習(xí)它最有效的方式是每一步設(shè)置一個(gè)參數(shù),檢查結(jié)果是否符合預(yù)期毛嫉。
先從最簡單的語法開始瞭恰,只設(shè)置 index='gender',通用語法如下:

data.pivot_table(index='gender')

從上表可以看到狱庇,年齡惊畏、身高、體重密任、甚至bool類型的是否吸煙都按某種聚合方式進(jìn)行了合并了⊙掌簦現(xiàn)在大概可以猜出 pivot_table() 函數(shù)中有個(gè)參數(shù)用來設(shè)置整合方式,而默認(rèn)值為平均浪讳。

設(shè)置"多行"為pivot

上例設(shè)置單個(gè) index缰盏,接下來看看設(shè)置多個(gè) index 的結(jié)果是什么樣的。這時(shí)用列表來存儲(chǔ)多個(gè) index淹遵。通用語法如下:

data.pivot_table(index=['gender','smoker'])

到目前為止口猜,我們只設(shè)置了 index,那為什么只在 age透揣、height和weight三列上做整合呢济炎?因?yàn)檫@兩列的值是數(shù)值型 (int, float),而其他列的值是非數(shù)值型 (object)辐真。

設(shè)定被整合的數(shù)據(jù)

如果只看 age列下的整合結(jié)果须尚,只需設(shè)置 values='age' 或者 values=['age'],通用語法如下:
pd.pivot_table(df, index=label_list, values=label_list)

data.pivot_table(index=['gender','smoker'],values='age')
設(shè)定整合函數(shù)

默認(rèn)整合函數(shù)是求平均侍咱,如果要用求和的函數(shù)需要設(shè)置 aggfunc=np.sum耐床,通用語法為
pd.pivot_table(df, index=label_list, values=label_list, aggfunc=func)

data.pivot_table(index=['gender','smoker'],values='age',aggfunc='sum')

aggfunc 參數(shù)可以被設(shè)置為多個(gè)函數(shù),用列表儲(chǔ)存楔脯,通用語法為
pd.pivot_table(df, index=label_list, values=label_list, aggfunc=func_list)

data.pivot_table(index=['gender','smoker'],values='age',aggfunc=['sum','mean'])
設(shè)定列為pivot

如果進(jìn)一步想看按吸煙劃分后的整合結(jié)果撩轰,可以設(shè)置 columns=[''smoker"]

data.pivot_table(index=['gender'],columns=['smoker'],values='age',aggfunc=['mean'])
若表結(jié)果中有 NaN ,可設(shè)置 fill_value=0 用零替代昧廷。
要看總計(jì)怎么辦堪嫂?設(shè)置 margins=True 就可以了。
aggfunc 參數(shù)還可以傳進(jìn)一個(gè)字典來實(shí)現(xiàn)不同列下應(yīng)用不同的整合函數(shù)麸粮,語法如下:

pd.pivot_table( df, index=["Counterparty","Trader","Category"], 
                    values=["Value","Quantity"],
                    aggfunc={"Value":np.sum, "Quantity":len},
                    fill_value=0,
                    margins=True )
再進(jìn)一步溉苛,不同列還可以應(yīng)用多個(gè)函數(shù),只需把函數(shù)名稱變成函數(shù)列表就可以了弄诲。語法如下:
pd.pivot_table( df, index=["Counterparty","Trader","Category"], 
                    values=["Value","Quantity"],
                    aggfunc={"Value":[np.sum, min, max], 
                             "Quantity":len},
                    fill_value=0 )

15.數(shù)據(jù)表的合并與連接

數(shù)據(jù)表可以按「鍵」合并愚战,用 merge 函數(shù)娇唯;可以按「軸」來連接,用 concat 函數(shù)寂玲。

15.1合并

按鍵 (key) 合并可以分「單鍵合并」和「多鍵合并」塔插。

單鍵合并

單鍵合并用merge函數(shù),語法如下:
pd.merge(df1,df2,how=s,on=c)
c是df1和df2共有的一欄拓哟,合并方式(how=s)有四種:
1.左連接(left join):合并之后顯示df1的所有行
2.右連接(right join):合并之后顯示df2的所有行
3.外連接(outer join)合并df1和df2的所有行
4.內(nèi)連接(inner join):合并所有行(默認(rèn)情況)

首先創(chuàng)建兩個(gè) DataFrame:
df_price:4 天的價(jià)格 (2019-01-01 到 2019-01-04)
df_volume:5 天的交易量 (2019-01-02 到 2019-01-06)

df_price = pd.DataFrame( {'Date': pd.date_range('2019-1-1', periods=4),
                          'Adj Close': [24.42, 25.00, 25.25, 25.64]})
df_volume = pd.DataFrame( {'Date': pd.date_range('2019-1-2', periods=5),
                           'Volume' : [56081400, 99455500, 83028700, 100234000, 73829000]})

接下來用 df_price 和 df_volume 展示四種合并想许。
left join:

pd.merge(df_price,df_volume,how='left')


right join:

pd.merge(df_price,df_volume,how='right')


outer join:

pd.merge(df_price,df_volume,how='outer')


inner join:

pd.merge(df_price,df_volume,how='inner')
多鍵合并

多鍵合并用的語法和單鍵合并一樣,只不過 on=c 中的 c 是多欄断序。
pd.merge( df1, df2, how=s, on=c )
首先創(chuàng)建兩個(gè) DataFrame:
portfolio1:3 比產(chǎn)品 FX Option, FX Swap 和 IR Option 的數(shù)量

portfolio2:4 比產(chǎn)品 FX Option (重復(fù)名稱), FX Swap 和 IR Swap 的數(shù)量

porfolio1 = pd.DataFrame({'Asset': ['FX', 'FX', 'IR'], 
                          'Instrument': ['Option', 'Swap', 'Option'], 
                          'Number': [1, 2, 3]})
porfolio2 = pd.DataFrame({'Asset': ['FX', 'FX', 'FX', 'IR'], 
                          'Instrument': ['Option', 'Option', 'Swap', 'Swap'], 
                          'Number': [4, 5, 6, 7]})

pd.merge(porfolio1,porfolio2,on=['Asset','Instrument'],how='left')
15.2連接

Numpy 數(shù)組可相互連接流纹,用 np.concat;同理违诗,Series 也可相互連接漱凝,DataFrame 也可相互連接,用 pd.concat诸迟。

連接Series

在 concat 函數(shù)也可設(shè)定參數(shù) axis:
axis = 0 (默認(rèn))茸炒,沿著軸 0 (行) 連接,得到一個(gè)更長的 Series

axis = 1阵苇,沿著軸 1 (列) 連接壁公,得到一個(gè) DataFrame

被連接的 Series 它們的 index 可以重復(fù) (overlapping),也可以不同绅项。

s1 = pd.Series([0, 1], index=['a', 'b'])
s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])
s3 = pd.Series([5, 6], index=['f', 'g'])
#沿著「軸 1」連接得到一個(gè) DataFrame紊册。
pd.concat([s1,s2,s3],axis=1)

將 s1 和 s3 沿「軸 0」連接來創(chuàng)建 s4,這樣 s4 和 s1 的 index 是有重復(fù)的趁怔。

s4 = pd.concat([s1,s3],axis=0)
pd.concat([s1,s4],axis=1)
#將 s1 和 s4 沿「軸 1」內(nèi)連接 (即只連接它們共有 index 對(duì)應(yīng)的值)
pd.concat([s1,s4],axis=1,join='inner')
連接DataFrame

連接 DataFrame 的邏輯和連接 Series 的一模一樣湿硝。
沿著行連接(axis=0):
先創(chuàng)建兩個(gè) DataFrame,df1 和 df2润努。

df1 = pd.DataFrame( np.arange(12).reshape(3,4), 
                    columns=['a','b','c','d'])
df2 = pd.DataFrame( np.arange(6).reshape(2,3),
                    columns=['b','d','a'])


沿著行連接分兩步
1.先把 df1 和 df2 列標(biāo)簽補(bǔ)齊
2.再把 df1 和 df2 縱向連起來

pd.concat([df1,df2],axis=0,ignore_index=True)

沿著列連接(axis=1):
先創(chuàng)建兩個(gè) DataFrame,df1 和 df2示括。

df1 = pd.DataFrame( np.arange(6).reshape(3,2), 
                    index=['a','b','c'],
                    columns=['one','two'] )
df2 = pd.DataFrame( 5 + np.arange(4).reshape(2,2), 
                    index=['a','c'], 
                    columns=['three','four'])
pd.concat([df1,df2],axis=1)

16.全局替換DataFrame.replace(to_replace, value)

http://www.reibang.com/p/2557a805211f
replace的基本結(jié)構(gòu)是:df.replace(to_replace, value) 前面是需要替換的值铺浇,后面是替換后的值。
例如我們要將南岸改為城區(qū):

image.png

這樣Python就會(huì)搜索整個(gè)DataFrame并將文檔中所有的南岸替換成了城區(qū)(要注意這樣的操作并沒有改變文檔的源數(shù)據(jù)垛膝,要改變?cè)磾?shù)據(jù)需要使用inplace = True)鳍侣。

17.data.to_csv()

不想要行索引。data.to_csv(index=False)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吼拥,一起剝皮案震驚了整個(gè)濱河市倚聚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凿可,老刑警劉巖惑折,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件授账,死亡現(xiàn)場離奇詭異,居然都是意外死亡惨驶,警方通過查閱死者的電腦和手機(jī)白热,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粗卜,“玉大人屋确,你說我怎么就攤上這事⌒樱” “怎么了攻臀?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纱昧。 經(jīng)常有香客問我刨啸,道長,這世上最難降的妖魔是什么砌些? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任呜投,我火速辦了婚禮,結(jié)果婚禮上存璃,老公的妹妹穿的比我還像新娘仑荐。我一直安慰自己,他們只是感情好纵东,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布粘招。 她就那樣靜靜地躺著,像睡著了一般偎球。 火紅的嫁衣襯著肌膚如雪洒扎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天衰絮,我揣著相機(jī)與錄音袍冷,去河邊找鬼。 笑死猫牡,一個(gè)胖子當(dāng)著我的面吹牛胡诗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淌友,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煌恢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了震庭?” 一聲冷哼從身側(cè)響起瑰抵,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎器联,沒想到半個(gè)月后二汛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婿崭,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年习贫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逛球。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苫昌,死狀恐怖颤绕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祟身,我是刑警寧澤奥务,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站袜硫,受9級(jí)特大地震影響氯葬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜婉陷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一帚称、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秽澳,春花似錦闯睹、人聲如沸粟判。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骚揍。三九已至,卻和暖如春胜嗓,著一層夾襖步出監(jiān)牢的瞬間员帮,已是汗流浹背惦蚊。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工亥贸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躬窜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓炕置,卻偏偏與公主長得像斩披,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讹俊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354