<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Pandas的apply, map, transform介紹和性能測試

          共 6908字,需瀏覽 14分鐘

           ·

          2023-03-04 09:18


          :Deephub Imba
          本文約8500,建議閱讀10分鐘
          本文介紹了如何使用 scikit-learn中的網(wǎng)格搜索功能來調(diào)整 PyTorch 深度學(xué)習(xí)模型的超參數(shù)。


          apply函數(shù)是我們經(jīng)常用到的一個Pandas操作。雖然這在較小的數(shù)據(jù)集上不是問題,但在處理大量數(shù)據(jù)時,由此引起的性能問題會變得更加明顯。雖然apply的靈活性使其成為一個簡單的選擇,但本文介紹了其他Pandas函數(shù)作為潛在的替代方案。


          在這篇文章中,我們將通過一些示例討論apply、agg、map和transform的預(yù)期用途。


          我們一個學(xué)生分?jǐn)?shù)為例


           df_english = pd.DataFrame(    {         "student": ["John", "James", "Jennifer"],         "gender": ["male", "male", "female"],         "score": [20, 30, 30],         "subject": "english"    } )
          df_math = pd.DataFrame( { "student": ["John", "James", "Jennifer"], "gender": ["male", "male", "female"], "score": [90, 100, 95], "subject": "math" } ) df = pd.concat( [df_english, df_math], ignore_index=True )



          map


           Series.map(arg, na_action=None) -> Series

          map方法適用于Series,它基于傳遞給函數(shù)的參數(shù)將每個值進行映射。arg可以是一個函數(shù)——就像apply可以取的一樣——也可以是一個字典或一個Series。


          na_action是指定序列的NaN值如何處理。當(dāng)設(shè)置為"ignore "時,arg將不會應(yīng)用于NaN值。


          例如想用映射替換性別的分類表示時:


           GENDER_ENCODING = {     "male": 0,     "female": 1 } df["gender"].map(GENDER_ENCODING)


          雖然apply不接受字典,但也可以完成同樣的操作。


           df["gender"].apply(lambda x:     GENDER_ENCODING.get(x, np.nan) )

          性能對比


          在對包含一百萬條記錄的gender序列進行編碼的簡單測試中,map比apply快10倍。


          random_gender_series = pd.Series([     random.choice(["male", "female"]) for _ in range(1_000_000) ])
          random_gender_series.value_counts()
          """ >>> female 500094 male 499906 dtype: int64 """

          看看對比結(jié)果


           """ map performance """ %%timeit random_gender_series.map(GENDER_ENCODING)
          # 41.4 ms ± 4.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
          """ apply performance """ %%timeit random_gender_series.apply(lambda x: GENDER_ENCODING.get(x, np.nan) )
          # 417 ms ± 5.32 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


          因為map也可以接受函數(shù),所以任何不依賴于其他元素的轉(zhuǎn)換操作都可以使用。比如使用map(len)或map(upper)這樣的東西可以讓預(yù)處理變得更容易。


          applymap


           DataFrame.applymap(func, na_action=None, **kwargs) -> DataFrame

          applymap與map非常相似,并且是使用apply內(nèi)部實現(xiàn)的。applymap就像map一樣,但是是在DataFrame上以elementwise的方式工作,但由于它是由apply內(nèi)部實現(xiàn)的,所以它不能接受字典或Series作為輸入——只允許使用函數(shù)。


           try:      df.applymap(dict())
          except TypeError as e: print("Only callables are valid! Error:", e)
          """ Only callables are valid! Error: the first argument must be callable """


          na_action的工作原理和map中的一樣。


          transform


           DataFrame.transform(func, axis=0, *args, **kwargs) -> DataFrame

          前兩個函數(shù)工作在元素級別,而transform工作在列級別。我們可以通過transform來使用聚合邏輯。


          假設(shè)要標(biāo)準(zhǔn)化數(shù)據(jù):


           df.groupby("subject")["score"] \    .transform(         lambda x: (x - x.mean()) / x.std()    )
          """ 0 -1.154701 1 0.577350 2 0.577350 3 -1.000000 4 1.000000 5 0.000000 Name: score, dtype: float64

          我們需要做的是從每個組中獲取分?jǐn)?shù),并用其標(biāo)準(zhǔn)化值替換每個元素。這肯定不能用map來實現(xiàn),因為它需要按列計算,而map只能按元素計算。


          如果使用熟悉apply,那么實現(xiàn)很簡單。


           df.groupby("subject")["score"] \    .apply(         lambda x: (x - x.mean()) / x.std()    )
          """ 0 -1.154701 1 0.577350 2 0.577350 3 -1.000000 4 1.000000 5 0.000000 Name: score, dtype: float64 """

          不僅本質(zhì)上,代碼基本上都是一樣的。那么transform有什么意義呢?


          Transform必須返回一個與它所應(yīng)用的軸長度相同的數(shù)據(jù)框架。


          也就是說即使transform與返回聚合值的groupby操作一起使用,它會將這些聚合值賦給每個元素。


          例如,假設(shè)我們想知道每門課所有學(xué)生的分?jǐn)?shù)之和。我們可以像這樣使用apply:


           df.groupby("subject")["score"] \    .apply( sum    )
          """ subject english 80 math 285 Name: score, dtype: int64 """


          但我們按學(xué)科匯總了分?jǐn)?shù),丟失了學(xué)生個體與其分?jǐn)?shù)之間的關(guān)聯(lián)信息。用transform做同樣的事情,我們會得到更有趣的東西:


           df.groupby("subject")["score"] \    .transform( sum    )
          """ 0 80 1 80 2 80 3 285 4 285 5 285 Name: score, dtype: int64 """


          因此,盡管我們在分組上操作,但仍然能夠得到組級信息與行級信息的關(guān)系。


          所以任何形式的聚合都會報錯,如果邏輯沒有返回轉(zhuǎn)換后的序列,transform將拋出ValueError。


           try:     df["score"].transform("mean") except ValueError as e:     print("Aggregation doesn't work with transform. Error:", e)
          """ Aggregation doesn't work with transform. Error: Function did not transform """


          而Apply的靈活性確保它即使使用聚合也能很好地工作。



           df["score"].apply("mean")
          """ 60.833333333333336 """


          性能對比


          就性能而言,transform的速度是apply的2倍。


           random_score_df = pd.DataFrame({     "subject": random.choices(["english", "math", "science", "history"], k=1_000_000),     "score": random.choices(list(np.arange(1, 100)), k=1_000_000) })


           """ Transform Performance Test """ %%timeit random_score_df.groupby("subject")["score"] \    .transform(         lambda x: (x - x.mean()) / x.std()    )
          """ 202 ms ± 5.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) """ """ Apply Performance Test """ %%timeit random_score_df.groupby("subject")["score"] \ .apply( lambda x: (x - x.mean()) / x.std() )
          """ 401 ms ± 5.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) """


          agg


           DataFrame.agg(func=None, axis=0, *args, **kwargs)     -> scalar | pd.Series | pd.DataFrame

          agg函數(shù)更容易理解,因為它只是返回傳遞給它的數(shù)據(jù)的聚合。所以無論自定義聚合器是如何實現(xiàn)的,結(jié)果都將是傳遞給它的每一列的單個值。


          來看看一個簡單的聚合——計算每個組在得分列上的平均值。


           df.groupby("subject")["score"].agg(mean_score="mean").round(2)


          多個聚合器也可以作為列表傳遞。


           df.groupby("subject")["score"].agg(    ["min", "mean", "max"] ).round(2)


          Agg提供了更多執(zhí)行聚合的選項。我們還可以構(gòu)建自定義聚合器,并對每一列執(zhí)行多個特定的聚合,例如計算一列的平均值和另一列的中值。


          性能對比


          就性能而言,agg比apply稍微快一些,至少對于簡單的聚合是這樣。


           random_score_df = pd.DataFrame({     "subject": random.choices(["english", "math", "science", "history"], k=1_000_000),     "score": random.choices(list(np.arange(1, 100)), k=1_000_000) })


           """ Agg Performance Test """
          %%timeit random_score_df.groupby("subject")["score"].agg("mean")
          """ 74.2 ms ± 5.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) """
          """ Apply Performance Test """
          %%timeit random_score_df.groupby("subject")["score"].apply(lambda x: x.mean()) """ 102.3 ms ± 1.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) """


          可以看到大約30%的性能提升。當(dāng)對多個聚合進行測試時,我們會得到類似的結(jié)果。


           """ Multiple Aggregators Performance Test with agg """ %%timeit random_score_df.groupby("subject")["score"].agg(    ["min", "mean", "max"] )
          """ 90.5 ms ± 16.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) """ """ Multiple Aggregators Performance Test with apply """ %%timeit random_score_df.groupby("subject")["score"].apply( lambda x: pd.Series( {"min": x.min(), "mean": x.mean(), "max": x.max()} ) ).unstack()
          """ 104 ms ± 5.78 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) """


          apply


          我們用它是因為它靈活。上面的每個例子都可以用apply實現(xiàn),但這種靈活性是有代價的:就像性能測試所證明的那樣,它明顯變慢了。


          apply的一些問題


          apply靈活性是非常好的,但是它也有一些問題,比如:


          從 2014 年開始,這個問題就一直困擾著 pandas。當(dāng)整個列中只有一個組時,就會發(fā)生這種情況。在這種情況下,即使 apply 函數(shù)預(yù)期返回一個Series,但最終會產(chǎn)生一個DataFrame。


          結(jié)果類似于額外的拆棧操作。我們這里嘗試重現(xiàn)它。我們將使用我們的原始數(shù)據(jù)框并添加一個城市列。假設(shè)我們的三個學(xué)生 John、James 和 Jennifer 都來自波士頓。


           df_single_group = df.copy() df_single_group["city"] = "Boston"



          讓我們計算兩組組的組均值:一組基于subject 列,另一組基于city。


          在subject 列上分組,我們得到了我們預(yù)期的多索引。


           df_single_group.groupby("subject").apply(lambda x: x["score"])


          但當(dāng)我們按city列分組時,只有一個組(對應(yīng)于“波士頓”),我們得到:


           df_single_group.groupby("city").apply(lambda x: x["score"])


          看到結(jié)果是如何旋轉(zhuǎn)的嗎?如果我們把這些疊起來,我們就會得到預(yù)期的結(jié)果。


           df_single_group.groupby("city").apply(lambda x: x["score"]).stack()


          在撰寫本文時,這個問題仍然沒有得到解決。


          總結(jié)


          apply提供的靈活性使其在大多數(shù)場景中成為非常方便的選擇,所以如果你的數(shù)據(jù)不大,或者對處理時間沒有硬性的要求,那就直接使用apply吧。如果真的對時間有要求,還是找到優(yōu)化的方式來操作,這樣可以省去大量的時間。


          本文代碼:
          https://github.com/Polaris000/BlogCode/blob/main/PandasApply/agg_apply_transform.ipynb


          編輯:王菁

          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香激情五月天 | 先锋影音亚洲无码av | 伊人大香蕉在线影院 | 99re在线视频免费观看 | 日韩精品熟妇 |