1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        用 Pyjanitor 更好地進(jìn)行數(shù)據(jù)清洗與處理

        共 13669字,需瀏覽 28分鐘

         ·

        2020-08-19 12:21


        隨著使用 Python 和 R 語言次數(shù)的增加,對(duì)于這兩門語言在數(shù)據(jù)科學(xué)領(lǐng)域的優(yōu)劣性有著深刻的體會(huì)。

        R 語言社區(qū)活躍且包豐富多樣,Tidyverse 風(fēng)潮更是讓這門語法怪異的編程語言煥發(fā)新生,也讓其在數(shù)據(jù)處理和分析的能力上更進(jìn)一步,但 R 語言相比于 Python 來說又缺乏了通用性;數(shù)據(jù)科學(xué)對(duì)于 Python 來說僅僅只是其中一個(gè)領(lǐng)域,隨著 Numpy 和 Pandas 構(gòu)建起來的生態(tài)圈蓬勃發(fā)展,也成為了一個(gè)與 R 語言在數(shù)據(jù)科學(xué)領(lǐng)域強(qiáng)有力的競(jìng)爭(zhēng)對(duì)手,但盡管 Pandas 已經(jīng)涵蓋了大部分我們平時(shí)處理和分析數(shù)據(jù)時(shí)的基本需求,可在流程和方法上卻又總比 R 語言匱乏不少。

        通常來說,如果是一些數(shù)據(jù)處理或清洗的工作或任務(wù),我更喜歡使用 R 語言,因?yàn)榈靡嬗?Hadly Wickham 等人的努力,R 語言有著一套舒服的操作流程,如管道操作符 %>%、函數(shù)式編程 purrr 包、使用 nest() 函數(shù)來構(gòu)造統(tǒng)一顆粒度的包裹性數(shù)據(jù)等。但在工作中使用一門編程語言往往既要考慮通用性,還要考慮團(tuán)隊(duì)的協(xié)作性,因此在實(shí)際工作中我使用更多的是 Python 而非 R 語言。

        在使用 Pandas 進(jìn)行數(shù)據(jù)處理時(shí),有時(shí)候會(huì)碰上一些本該很容易處理但卻還要額外多定義一個(gè)函數(shù)的情況。

        比如我數(shù)據(jù)中有兩個(gè)字段 ab,但是兩個(gè)字段或多或少都有缺失值。

        In [2]: import pandas as pd
           ...: import numpy as np
           ...:
           ...: df = pd.DataFrame(
           ...:     {
           ...:         "a": [None2NoneNone56],
           ...:         "b": [1NoneNone4None6]
           ...:     }
           ...: )
           ...: df
        Out[2]:
             a    b
        0  NaN  1.0
        1  2.0  NaN
        2  NaN  NaN
        3  NaN  4.0
        4  5.0  NaN
        5  6.0  6.0

        所以我需要定義一個(gè)新的字段 c,它由兩個(gè)字段構(gòu)建而來。如果第一個(gè)字段中存在缺失值,則取第二個(gè)字段中的值,反之亦可;如果兩者都為缺失,則保留缺失值。

        為了實(shí)現(xiàn)這個(gè)目的通常來說都是定義一個(gè)函數(shù),然后用 apply() 方法來生成:

        In [3]: def get_valid_value(col_x, col_y):
           ...:     if not pd.isna(col_x) and pd.isna(col_y):
           ...:         return col_x
           ...:     elif pd.isna(col_x) and not pd.isna(col_y):
           ...:         return col_y
           ...:     elif not (pd.isna(col_x) or pd.isna(col_y)):
           ...:         return col_x
           ...:     else:
           ...:         return np.nan
           ...:
           ...: df['c'] = df.apply(lambda x: get_valid_value(x['a'], x['b']), axis=1)
           ...: df
        Out[3]:
             a    b    c
        0  NaN  1.0  1.0
        1  2.0  NaN  2.0
        2  NaN  NaN  NaN
        3  NaN  4.0  4.0
        4  5.0  NaN  5.0
        5  6.0  6.0  6.0

        這種需求其實(shí)很常見,在 SQL 中存在 coalesc() 這樣一個(gè)函數(shù),實(shí)現(xiàn)的就是上述我所描述的這種拼湊字段的做法;在 R 語言的 dplyr 包中也已經(jīng)實(shí)現(xiàn)了 SQL 同名函數(shù)一樣的方法。而 Pandas 只實(shí)現(xiàn)了不同 DataFrame 間的方法 DataFrame.combine(),并沒有實(shí)現(xiàn)單個(gè) DataFrame 中字段的 coalesc() 方法。但好在 pyjanitor 彌補(bǔ)了 Pandas 在處理數(shù)據(jù)時(shí)的一些不足,而且也能更好地嵌入到我們的工作流中。這也就是為什么本文要談?wù)?pyjanitor 的原因。

        與鏈?zhǔn)椒椒ňo密結(jié)合的操作方式

        pyjanitor 庫的靈感來自于 R 語言的 janitor 包,英文單詞即為清潔工之意,也就是通常用來進(jìn)行數(shù)據(jù)處理或清洗數(shù)據(jù)。pyjanitor 脫胎于 Pandas 生態(tài)圈,其使用的核心也是圍繞著鏈?zhǔn)秸归_,可以使得我們更加專注于每一步操作的動(dòng)作或謂詞(Verbs)

        pyjanitor 的 API 文檔并不復(fù)雜,大多數(shù) API 都是圍繞著通用的清洗任務(wù)而設(shè)計(jì)。這主要涉及為幾部分:

        • 操作的方法(Modify columns)

        • 操作的方法(Modify values)

        • 用于篩選的方法(Filtering)

        • 用于數(shù)據(jù)預(yù)處理的方法(Preprocessing),主要是機(jī)器學(xué)習(xí)特征處理的一些方法

        • 其他方法

        由于篇幅有限,不能將每個(gè)方法都一一舉例,這里我就只挑其中幾個(gè)方法給出使用示例。

        需要注意的是,盡管 pyjanitor 庫名稱帶有 py 二字,但是在導(dǎo)入時(shí)則是輸入 janitor;就像 Beautifulsoup4 庫在導(dǎo)入時(shí)寫為 bs4 一樣,以免無法導(dǎo)入而報(bào)錯(cuò)。

        coalesc

        有了 pyjanitor 之后,開頭我舉的例子其實(shí)就可以通過 coalesc() 方法來快速實(shí)現(xiàn),就像這樣:

        In [8]: import pandas as pd
           ...: import janitor
           ...:
           ...: df = pd.DataFrame(
           ...:     {
           ...:         "a": [None2NoneNone56],
           ...:         "b": [1NoneNone4None6]
           ...:     }
           ...: )
           ...:
           ...: df.coalesce(column_names=['a','b'],
           ...:             new_column_name='c',
           ...:             delete_columns=False)
        Out[8]:
             a    b    c
        0  NaN  1.0  1.0
        1  2.0  NaN  2.0
        2  NaN  NaN  NaN
        3  NaN  4.0  4.0
        4  5.0  NaN  5.0
        5  6.0  6.0  6.0

        從結(jié)果上可以看到,我們不需要再額外寫一個(gè)方法,直接就可以以符合直覺的方式來完成相應(yīng)的操作。

        concatenate_columns 和 deconcatnate_column

        如果你有使用過 R 語言 tidyr 包的 unite() 函數(shù)和 separate() 函數(shù),那么其實(shí)使用 pyjanitorconcatenate_columns()deconcatnate_column() 就不會(huì)陌生,前者是將多個(gè)列根據(jù)某個(gè)分隔符合并成一個(gè)新列,而后者則是將單個(gè)列拆分成多個(gè)列。這里我們假設(shè)數(shù)據(jù)中有一個(gè)關(guān)于日期時(shí)間的字段,圍繞這個(gè)字段來進(jìn)行演示:

        In [1]: import pandas as pd
           ...: import janitor
           ...:
           ...: df = pd.DataFrame({"date_time": ["2020-02-01 11:00:00",
           ...:                                  "2020-02-03 12:10:11",
           ...:                                  "2020-03-24 13:24:31"]})

        In [2]: (
           ...:     df
           ...:     .deconcatenate_column(
           ...:         column_name="date_time",
           ...:         new_column_names=['date''time'],
           ...:         sep=' ',
           ...:         preserve_position=False
           ...:     )
           ...:     .deconcatenate_column(
           ...:         column_name="date",
           ...:         new_column_names=['year''month''day'],
           ...:         sep='-',
           ...:         preserve_position=True
           ...:     )
           ...:     .concatenate_columns(
           ...:         column_names=['year''month''day'],
           ...:         new_column_name='new_date',
           ...:         sep='-'
           ...:     )
           ...: )
        Out[2]:
                     date_time  year month day      time    new_date
        0  2020-02-01 11:00:00  2020    02  01  11:00:00  2020-02-01
        1  2020-02-03 12:10:11  2020    02  03  12:10:11  2020-02-03
        2  2020-03-24 13:24:31  2020    03  24  13:24:31  2020-03-24

        這個(gè)例子可能有些無聊,但是能很清楚地看到這兩個(gè)方法幫我們順利地將數(shù)據(jù)中的字段進(jìn)行拆分和合并,雖然說我們可以直接通過 assign() 方法來實(shí)現(xiàn)變量賦值,但是不可避免的要寫三遍;同時(shí)盡管 Pandas 已經(jīng)可以通過 str.split(sep, expand=True) 的方式來對(duì)字符類型字段進(jìn)行分隔并轉(zhuǎn)換成相應(yīng)的字段,但是最后返回的是一個(gè)新的 DataFrame,不能直接和原有的數(shù)據(jù)合并在一起。

        從結(jié)果中我們可以看到,pyjanitor 提供的方法可以幫助我們很好地保持?jǐn)?shù)據(jù)的一致性和統(tǒng)一性。

        take_first

        有的時(shí)候,我們會(huì) groupby() 某個(gè)字段并對(duì)一些數(shù)值列進(jìn)行操作、倒序排列,最后每組取最大的數(shù)即倒序后的第一行。在 R 語言中我們可以很輕易直接這么實(shí)現(xiàn):

        library(dplyr)

        df <- data.frame(a = c("x""x""y""y""y"),
                         b = c(13254))

        df %>% 
          group_by(a) %>% 
          arrange(desc(b)) %>% 
          slice(1) %>% 
          ungroup()

        #  A tibble: 2 x 2
        #   a         b
        #     
        # 1 x         3
        # 2 y         5

        在沒使用 pyjanitor 之前,我往往都是通過 Pandas 這么實(shí)現(xiàn)的:

        In [1]: import pandas as pd
           ...:
           ...: df = pd.DataFrame({"a":["x""x""y""y""y"],
           ...:                    "b":[1,3,2,5,4]})
           ...: (
           ...:     df
           ...:     .groupby("a")
           ...:     .apply(lambda grp: grp
           ...:                 .sort_values(by="b", ascending=False)
           ...:                 .head(1))
           ...:     .reset_index(drop=True)
           ...: )
        Out[1]:
           a  b
        0  x  3
        1  y  5

        這里利用了 groupby 之后的生成的 DataFrameGroupBy 對(duì)象再進(jìn)行多余的降序取第一個(gè)的操作,最后將分組后產(chǎn)生的索引值刪除。現(xiàn)在可以直接使用 pyjanitor 中的 take_first 方法直接一步到位:

        In [1]: import pandas as pd
           ...: import janitor
           ...:
           ...: df = pd.DataFrame({"a":["x""x""y""y""y"],
           ...:                    "b":[1,3,2,5,4]})
           ...: df.take_first(subset="a", by="b", ascending=False)
        Out[1]:
           a  b
        3  y  5
        1  x  3

        除了以上列舉的方法之外,還有許多方法等待各位去探索,詳見官方文檔,官方文檔上還貼心的給出了一些實(shí)際的用法和案例;只要你熟練使用了 Pandas 那么很快就能掌握 pyjanitor 庫的大部分方法。

        「有 Pandas 內(nèi)味兒」——實(shí)現(xiàn)你的 janitor 方法

        pyjanitor 中的方法僅僅只是一些通用的實(shí)現(xiàn)方法,不同的人在使用過程中可能也會(huì)有不同的需要。但好在我們也可以實(shí)現(xiàn)自己的 「janitor」 方法。

        pyjanitor 得益于 pandas-flavor 庫的加持得以輕松實(shí)現(xiàn)鏈?zhǔn)椒椒?,鏈?zhǔn)椒椒ǖ暮?jiǎn)單實(shí)現(xiàn)原理見我之前的文章《5 分鐘解讀 Python 中的鏈?zhǔn)秸{(diào)用》。

        pandas-flavor 提供了能讓使用者簡(jiǎn)單且快速地編寫出**帶有「 Pandas 味兒」**的方法:

        1. 第一步,只需要在你編寫的函數(shù)、方法或類中添加對(duì)應(yīng)的裝飾器即可;

        2. 第二步,確保最后返回的是 DataFrame 或 Series 類的對(duì)象即可。

        比如我們寫一個(gè)簡(jiǎn)單清理數(shù)據(jù)字段或變量名稱多余空格的方法:

        import pandas as pd
        import pandas_flavor as pf

        @pf.register_dataframe_method
        def strip_names(df):
            import re

            colnames = df.columns.tolist()
            colnames = list(map(lambda col: '_'.join(re.findall(r"\w+", col)), colnames))
            df.columns = colnames
            return df

        最后結(jié)果如下:

        In [14]: data = pd.DataFrame({" a ": [1,1], "  b  zz  ": [2,1]})
            ...: data
        Out[14]:
            a     b  zz
        0    1          2
        1    1          1

        In [15]: data.strip_names()
        Out[15]:
           a  b_zz
        0  1     2
        1  1     1

        本質(zhì)上來說,pandas-flavor 庫中提供的裝飾器就等價(jià)于重寫或新增了 DataFrame 類的方法,在使用過程中如果方法有報(bào)錯(cuò),那就需要還原加載 pandas 庫之后再重新寫入。

        關(guān)于 pandas-flavor 裝飾器的用法,詳見項(xiàng)目的 Github(https://github.com/Zsailer/pandas_flavor

        結(jié)尾

        通過 pyjanitor 庫我們可以更進(jìn)一步地豐富我們?cè)谔幚頂?shù)據(jù)時(shí)的工作流,并且借助鏈?zhǔn)椒椒ǖ奶匦詠砜s短數(shù)據(jù)分析或挖掘過程的耗時(shí)。

        但也正如我在之前談?wù)撚嘘P(guān)鏈?zhǔn)秸{(diào)用的文章中所提到的,隨著鏈?zhǔn)秸{(diào)用的方法或過程的增多,出錯(cuò)的幾率也會(huì)大大增加。只有當(dāng)你確定以及肯定經(jīng)過每一步處理后返回的結(jié)果與你預(yù)期中的呈現(xiàn)形式相符時(shí),才能保證鏈?zhǔn)椒椒ㄦ湹姆€(wěn)健。

        無論如何,pyjanitor 從一定程度上也擴(kuò)展了 Pandas 生態(tài)在處理數(shù)據(jù)上的多樣性和玩法。

        作者:100gle,練習(xí)時(shí)長(zhǎng)不到兩年的非正經(jīng)文科生一枚,喜歡敲代碼、寫寫文章、搗鼓搗鼓各種新事物;現(xiàn)從事有關(guān)大數(shù)據(jù)分析與挖掘的相關(guān)工作。


        贊 賞 作 者



        Python中文社區(qū)作為一個(gè)去中心化的全球技術(shù)社區(qū),以成為全球20萬Python中文開發(fā)者的精神部落為愿景,目前覆蓋各大主流媒體和協(xié)作平臺(tái),與阿里、騰訊、百度、微軟、亞馬遜、開源中國(guó)、CSDN等業(yè)界知名公司和技術(shù)社區(qū)建立了廣泛的聯(lián)系,擁有來自十多個(gè)國(guó)家和地區(qū)數(shù)萬名登記會(huì)員,會(huì)員來自以工信部、清華大學(xué)、北京大學(xué)、北京郵電大學(xué)、中國(guó)人民銀行、中科院、中金、華為、BAT、谷歌、微軟等為代表的政府機(jī)關(guān)、科研單位、金融機(jī)構(gòu)以及海內(nèi)外知名公司,全平臺(tái)近20萬開發(fā)者關(guān)注。

        長(zhǎng)按掃碼添加“Python小助手” 

        進(jìn)入 P Y 交 流 群

        ▼點(diǎn)擊成為社區(qū)會(huì)員   喜歡就點(diǎn)個(gè)在看吧

        瀏覽 37
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品中文字幕av | 午夜成人精品 | 国精无码欧精品亚洲一区蜜桃 | 国产性爱自拍偷拍 | 奇米7777影视精品人人爽 | 亚洲综合电影网 | 成人永久免费视频在线观看 | 巨乳粉逼| 91精品丝袜国产高跟在线 | 肏屄在线观看视频 |