乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      不再糾結(jié),一文詳解pandas中的map、apply、applymap、groupby、agg......

       LibraryPKU 2020-02-02

      文章的數(shù)據(jù)和代碼都已上傳至我的github倉庫:https://github.com/CNFeffery/DataScienceStudyNotes

      一、簡介

      pandas提供了很多方便簡潔的方法,用于對單列、多列數(shù)據(jù)進(jìn)行批量運(yùn)算或分組聚合運(yùn)算,熟悉這些方法后可極大地提升數(shù)據(jù)分析的效率,也會使得你的代碼更加地優(yōu)雅簡潔。

      本文就將針對pandas中的map()、apply()、applymap()groupby()、agg()等方法展開詳細(xì)介紹,并結(jié)合實(shí)際例子幫助大家更好地理解它們的使用技巧。

      二、非聚合類方法

      這里的非聚合指的是數(shù)據(jù)處理前后沒有進(jìn)行分組操作,數(shù)據(jù)列的長度沒有發(fā)生改變,因此本章節(jié)中不涉及groupby()。

      首先讀入數(shù)據(jù),這里使用到的全美嬰兒姓名數(shù)據(jù),包含了1880-2018年全美每年對應(yīng)每個(gè)姓名的新生兒數(shù)據(jù),在jupyterlab中讀入數(shù)據(jù)并打印數(shù)據(jù)集的一些基本信息以了解我們的數(shù)據(jù)集:

      import pandas as pd

      #讀入數(shù)據(jù)
      data = pd.read_csv('data.csv')
      data.head()
      #查看各列數(shù)據(jù)類型、數(shù)據(jù)框行列數(shù)
      print(data.dtypes)
      print()
      print(data.shape)

      2.1 map()

      類似Python內(nèi)建的map()方法,pandas中的map()方法將函數(shù)、字典索引或是一些需要接受單個(gè)輸入值的特別的對象與對應(yīng)的單個(gè)列的每一個(gè)元素建立聯(lián)系并串行得到結(jié)果。

      譬如這里我們想要得到gender列的F、M轉(zhuǎn)換為女性、男性的新列,可以有以下幾種實(shí)現(xiàn)方式:

      • 字典映射

      這里我們編寫F、M與女性、男性之間一一映射的字典,再利用map()方法來得到映射列:

      #定義F->女性,M->男性的映射字典
      gender2xb = {'F': '女性', 'M': '男性'}
      #利用map()方法得到對應(yīng)gender列的映射列
      data.gender.map(gender2xb)

      • lambda函數(shù)

      這里我們向map()中傳入lambda函數(shù)來實(shí)現(xiàn)所需功能:

      #因?yàn)橐呀?jīng)知道數(shù)據(jù)gender列性別中只有F和M所以編寫如下lambda函數(shù)
      data.gender.map(lambda x:'女性' if x is 'F' else '男性')
      • 常規(guī)函數(shù)

      也可以傳入def定義的常規(guī)函數(shù):

      def gender_to_xb(x):

      return '女性' if x is 'F' else '男性'

      data.gender.map(gender_to_xb)

      map()可以傳入的內(nèi)容有時(shí)候可以很特殊,如下面的例子:

      • 特殊對象

      一些接收單個(gè)輸入值且有輸出的對象也可以用map()方法來處理:

      data.gender.map('This kid's gender is {}'.format)

      map()還有一個(gè)參數(shù)na_action,類似R中的na.action,取值為Noneingore,用于控制遇到缺失值的處理方式,設(shè)置為ingore時(shí)串行運(yùn)算過程中將忽略Nan值原樣返回。

      2.2 apply()

      apply()堪稱pandas中最好用的方法,其使用方式跟map()很像,主要傳入的主要參數(shù)都是接受輸入返回輸出。

      但相較于map()針對單列Series進(jìn)行處理,一條apply()語句可以對單列或多列進(jìn)行運(yùn)算,覆蓋非常多的使用場景。

      下面我們來分別介紹:

      • 單列數(shù)據(jù)

      這里我們參照2.1向apply()中傳入lambda函數(shù):

      data.gender.apply(lambda x:'女性' if x is 'F' else '男性')

      可以看到這里實(shí)現(xiàn)了跟map()一樣的功能。

      • 輸入多列數(shù)據(jù)

      apply()最特別的地方在于其可以同時(shí)處理多列數(shù)據(jù),我們先來了解一下如何處理多列數(shù)據(jù)輸入單列數(shù)據(jù)輸出的情況。

      譬如這里我們編寫一個(gè)使用到多列數(shù)據(jù)的函數(shù)用于拼成對于每一行描述性的話,并在apply()用lambda函數(shù)傳遞多個(gè)值進(jìn)編寫好的函數(shù)中(當(dāng)調(diào)用DataFrame.apply()時(shí),apply()在串行過程中實(shí)際處理的是每一行數(shù)據(jù),而不是Series.apply()那樣每次處理單個(gè)值)。

      注意在處理多個(gè)值時(shí)要給apply()添加參數(shù)axis=1

      def generate_descriptive_statement(year, name, gender, count):
      year, count = str(year), str(count)
      gender = '女性' if gender is 'F' else '男性'

      return '在{}年,叫做{}性別為{}的新生兒有{}個(gè)。'.format(year, name, gender, count)

      data.apply(lambda row:generate_descriptive_statement(row['year'],
      row['name'],
      row['gender'],
      row['count']),
      axis = 1)


      • 輸出多列數(shù)據(jù)

      有些時(shí)候我們利用apply()會遇到希望同時(shí)輸出多列數(shù)據(jù)的情況,在apply()中同時(shí)輸出多列時(shí)實(shí)際上返回的是一個(gè)Series,這個(gè)Series中每個(gè)元素是與apply()中傳入函數(shù)的返回值順序?qū)?yīng)的元組。

      比如下面我們利用apply()來提取name列中的首字母和剩余部分字母:

      data.apply(lambda row: (row['name'][0], row['name'][1:]), axis=1)

      可以看到,這里返回的是單列結(jié)果,每個(gè)元素是返回值組成的元組,這時(shí)若想直接得到各列分開的結(jié)果,需要用到zip(*zipped)來解開元組序列,從而得到分離的多列返回值:

      a, b = zip(*data.apply(lambda row: (row['name'][0], row['name'][1:]), axis=1))
      print(a[:10])
      print(b[:10])
      • 結(jié)合tqdm給apply()過程添加進(jìn)度條

      我們知道apply()在運(yùn)算時(shí)實(shí)際上仍然是一行一行遍歷的方式,因此在計(jì)算量很大時(shí)如果有一個(gè)進(jìn)度條來監(jiān)視運(yùn)行進(jìn)度就很舒服。

      tqdm:用于添加代碼進(jìn)度條的第三方庫

      tqdm對pandas也是有著很好的支持。

      我們可以使用progress_apply()代替apply(),并在運(yùn)行progress_apply()之前添加tqdm.tqdm.pandas(desc='')來啟動(dòng)對apply過程的監(jiān)視。

      其中desc參數(shù)傳入對進(jìn)度進(jìn)行說明的字符串,下面我們在上一小部分示例的基礎(chǔ)上進(jìn)行改造來添加進(jìn)度條功能:

      from tqdm import tqdm

      def generate_descriptive_statement(year, name, gender, count):
      year, count = str(year), str(count)
      gender = '女性' if gender is 'F' else '男性'

      return '在{}年,叫做{}性別為{}的新生兒有{}個(gè)。'.format(year, name, gender, count)

      #啟動(dòng)對緊跟著的apply過程的監(jiān)視
      tqdm.pandas(desc='apply')
      data.progress_apply(lambda row:generate_descriptive_statement(row['year'],
      row['name'],
      row['gender'],
      row['count']),
      axis = 1)

      可以看到在jupyter lab中運(yùn)行程序的過程中,下方出現(xiàn)了監(jiān)視過程的進(jìn)度條,這樣就可以實(shí)時(shí)了解apply過程跑到什么地方了。

      • 結(jié)合tqdm_notebook()apply()過程添加美觀進(jìn)度條

      熟悉tqdm的朋友都知道其針對jupyter notebook開發(fā)了ui更加美觀的tqdm_notebook()

      而要想在jupyter notebook/jupyter lab平臺上為pandas的apply過程添加美觀進(jìn)度條,可以參照如下示例:

      from tqdm._tqdm_notebook import tqdm_notebook

      tqdm_notebook.pandas(desc='apply')
      data.progress_apply(lambda row:generate_descriptive_statement(row['year'],
      row['name'],
      row['gender'],
      row['count']),
      axis = 1)

      這時(shí)所添加的進(jìn)度條就美觀了不少。

      2.3  applymap()

      applymap()是與map()方法相對應(yīng)的專屬于DataFrame對象的方法,類似map()方法傳入函數(shù)、字典等,傳入對應(yīng)的輸出結(jié)果。

      不同的是applymap()將傳入的函數(shù)等作用于整個(gè)數(shù)據(jù)框中每一個(gè)位置的元素,因此其返回結(jié)果的形狀與原數(shù)據(jù)框一致。

      譬如下面的簡單示例,我們把嬰兒姓名數(shù)據(jù)中所有的字符型數(shù)據(jù)消息小寫化處理,對其他類型則原樣返回:

      def lower_all_string(x):
      if isinstance(x, str):
      return x.lower()
      else:
      return x

      data.applymap(lower_all_string)

      其形狀沒有變化:

      配合applymap(),可以簡潔地完成很多數(shù)據(jù)處理操作。

      三、聚合類方法

      有些時(shí)候我們需要像SQL里的聚合操作那樣將原始數(shù)據(jù)按照某個(gè)或某些離散型的列進(jìn)行分組再求和、平均數(shù)等聚合之后的值,在pandas中分組運(yùn)算是一件非常優(yōu)雅的事。

      3.1 利用groupby()進(jìn)行分組

      要進(jìn)行分組運(yùn)算第一步當(dāng)然就是分組,在pandas中對數(shù)據(jù)框進(jìn)行分組使用到groupby()方法。

      其主要使用到的參數(shù)為by,這個(gè)參數(shù)用于傳入分組依據(jù)的變量名稱,當(dāng)變量為1個(gè)時(shí)傳入名稱字符串即可。

      當(dāng)為多個(gè)時(shí)傳入這些變量名稱列表,DataFrame對象通過groupby()之后返回一個(gè)生成器,需要將其列表化才能得到需要的分組后的子集,如下面的示例:

      #按照年份和性別對嬰兒姓名數(shù)據(jù)進(jìn)行分組
      groups = data.groupby(by=['year','gender'])
      #查看groups類型
      type(groups)

      可以看到它此時(shí)是生成器,下面我們用列表解析的方式提取出所有分組后的結(jié)果:

      #利用列表解析提取分組結(jié)果
      groups = [group for group in groups]

      查看其中的一個(gè)元素:

      可以看到每一個(gè)結(jié)果都是一個(gè)二元組,元組的第一個(gè)元素是對應(yīng)這個(gè)分組結(jié)果的分組組合方式,第二個(gè)元素是分組出的子集數(shù)據(jù)框,而對于DataFrame.groupby()得到的結(jié)果。

      主要可以進(jìn)行以下幾種操作:

      • 直接調(diào)用聚合函數(shù)

      譬如這里我們提取count列后直接調(diào)用max()方法:

      #求每個(gè)分組中最高頻次
      data.groupby(by=['year','gender'])['count'].max()

      注意這里的year、gender列是以索引的形式存在的,想要把它們還原回?cái)?shù)據(jù)框,使用reset_index(drop=False)即可:


      • 結(jié)合apply()

      分組后的結(jié)果也可以直接調(diào)用apply(),這樣可以編寫更加自由的函數(shù)來完成需求,譬如下面我們通過自編函數(shù)來求得每年每種性別出現(xiàn)頻次最高的名字及對應(yīng)頻次。

      要注意的是,這里的apply傳入的對象是每個(gè)分組之后的子數(shù)據(jù)框,所以下面的自編函數(shù)中直接接收的df參數(shù)即為每個(gè)分組的子數(shù)據(jù)框:

      import numpy as np

      def find_most_name(df):
      return str(np.max(df['count']))+'-'+df['name'][np.argmax(df['count'])]

      data.groupby(['year','gender']).apply(find_most_name).reset_index(drop=False)


      3.2 利用agg()進(jìn)行更靈活的聚合

      agg即aggregate,聚合,在pandas中可以利用agg()對Series、DataFrame以及groupby()后的結(jié)果進(jìn)行聚合。

      其傳入的參數(shù)為字典,鍵為變量名,值為對應(yīng)的聚合函數(shù)字符串,譬如{'v1':['sum','mean'], 'v2':['median','max','min]}就代表對數(shù)據(jù)框中的v1列進(jìn)行求和、均值操作,對v2列進(jìn)行中位數(shù)、最大值、最小值操作。

      下面用幾個(gè)簡單的例子演示其具體使用方式:

      • 聚合Series

      在對Series進(jìn)行聚合時(shí),因?yàn)橹挥?列,所以可以不使用字典的形式傳遞參數(shù),直接傳入函數(shù)名列表即可:

      #求count列的最小值、最大值以及中位數(shù)
      data['count'].agg(['min','max','median'])
      • 聚合數(shù)據(jù)框

      對數(shù)據(jù)框進(jìn)行聚合時(shí)因?yàn)橛卸嗔校砸褂米值涞姆绞絺魅刖酆戏桨福?/p>

      data.agg({'year': ['max','min'], 'count': ['mean','std']})

      值得注意的是,因?yàn)樯侠袑τ诓煌兞康木酆戏桨覆唤y(tǒng)一,所以會出現(xiàn)NaN的情況。

      • 聚合groupby()結(jié)果
      data.groupby(['year','gender']).agg({'count':['min','max','median']}).reset_index(drop=False)

      可以注意到雖然我們使用reset_index()將索引列還原回變量,但聚合結(jié)果的列名變成紅色框中奇怪的樣子,而在pandas 0.25.0以及之后的版本中,可以使用pd.NamedAgg()來為聚合后的每一列賦予新的名字:

      data.groupby(['year','gender']).agg(
      min_count=pd.NamedAgg(column='count', aggfunc='min'),
      max_count=pd.NamedAgg(column='count', aggfunc='max'),
      median=pd.NamedAgg(column='count', aggfunc='median')).reset_index(drop=False)

      以上就是本文全部內(nèi)容,如有筆誤望指出!

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多