<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 第 10 章 數據聚合與分組運算 對數據集進行分組并對各組應用一個函數(無論是聚合還是轉換),通常是數據分析工作中的重要環節。在將數據集加載、融合、準備好之后,通常就是計算分組統計或生成透視表。pandas提供了一個靈活高效的gruopby功能,它使你能以一種自然的方式對數據集進行切片、切塊、摘要等操作。 關系型數據庫和SQL(Structured Query Language,結構化查詢語言)能夠如此流行的原因之一就是其能夠方便地對數據進行連接、過濾、轉換和聚合。但是,像SQL這樣的查詢語言所能執行的分組運算的種類很有限。在本章中你將會看到,由于Python和pandas強大的表達能力,我們可以執行復雜得多的分組運算(利用任何可以接受pandas對象或NumPy數組的函數)。在本章中,你將會學到: - 使用一個或多個鍵(形式可以是函數、數組或DataFrame列名)分割pandas對象。 - 計算分組的概述統計,比如數量、平均值或標準差,或是用戶定義的函數。 - 應用組內轉換或其他運算,如規格化、線性回歸、排名或選取子集等。 - 計算透視表或交叉表。 - 執行分位數分析以及其它統計分組分析。 >筆記:對時間序列數據的聚合(groupby的特殊用法之一)也稱作重采樣(resampling),本書將在第11章中單獨對其進行講解。 # 10.1 GroupBy機制 Hadley Wickham(許多熱門R語言包的作者)創造了一個用于表示分組運算的術語"split-apply-combine"(拆分-應用-合并)。第一個階段,pandas對象(無論是Series、DataFrame還是其他的)中的數據會根據你所提供的一個或多個鍵被拆分(split)為多組。拆分操作是在對象的特定軸上執行的。例如,DataFrame可以在其行(axis=0)或列(axis=1)上進行分組。然后,將一個函數應用(apply)到各個分組并產生一個新值。最后,所有這些函數的執行結果會被合并(combine)到最終的結果對象中。結果對象的形式一般取決于數據上所執行的操作。圖10-1大致說明了一個簡單的分組聚合過程。 ![圖10-1 分組聚合演示](https://img.kancloud.cn/24/28/2428dc9ee8d50be4b266882d555feddd_1038x896.png) 分組鍵可以有多種形式,且類型不必相同: - 列表或數組,其長度與待分組的軸一樣。 - 表示DataFrame某個列名的值。 - 字典或Series,給出待分組軸上的值與分組名之間的對應關系。 - 函數,用于處理軸索引或索引中的各個標簽。 注意,后三種都只是快捷方式而已,其最終目的仍然是產生一組用于拆分對象的值。如果覺得這些東西看起來很抽象,不用擔心,我將在本章中給出大量有關于此的示例。首先來看看下面這個非常簡單的表格型數據集(以DataFrame的形式): ```python In [10]: df = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], ....: 'key2' : ['one', 'two', 'one', 'two', 'one'], ....: 'data1' : np.random.randn(5), ....: 'data2' : np.random.randn(5)}) In [11]: df Out[11]: data1 data2 key1 key2 0 -0.204708 1.393406 a one 1 0.478943 0.092908 a two 2 -0.519439 0.281746 b one 3 -0.555730 0.769023 b two 4 1.965781 1.246435 a one ``` 假設你想要按key1進行分組,并計算data1列的平均值。實現該功能的方式有很多,而我們這里要用的是:訪問data1,并根據key1調用groupby: ```python In [12]: grouped = df['data1'].groupby(df['key1']) In [13]: grouped Out[13]: <pandas.core.groupby.SeriesGroupBy object at 0x7faa31537390> ``` 變量grouped是一個GroupBy對象。它實際上還沒有進行任何計算,只是含有一些有關分組鍵df['key1']的中間數據而已。換句話說,該對象已經有了接下來對各分組執行運算所需的一切信息。例如,我們可以調用GroupBy的mean方法來計算分組平均值: ```python In [14]: grouped.mean() Out[14]: key1 a 0.746672 b -0.537585 Name: data1, dtype: float64 ``` 稍后我將詳細講解.mean()的調用過程。這里最重要的是,數據(Series)根據分組鍵進行了聚合,產生了一個新的Series,其索引為key1列中的唯一值。之所以結果中索引的名稱為key1,是因為原始DataFrame的列df['key1']就叫這個名字。 如果我們一次傳入多個數組的列表,就會得到不同的結果: ```python In [15]: means = df['data1'].groupby([df['key1'], df['key2']]).mean() In [16]: means Out[16]: key1 key2 a one 0.880536 two 0.478943 b one -0.519439 two -0.555730 Name: data1, dtype: float64 ``` 這里,我通過兩個鍵對數據進行了分組,得到的Series具有一個層次化索引(由唯一的鍵對組成): ```python In [17]: means.unstack() Out[17]: key2 one two key1 a 0.880536 0.478943 b -0.519439 -0.555730 ``` 在這個例子中,分組鍵均為Series。實際上,分組鍵可以是任何長度適當的數組: ```python In [18]: states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio']) In [19]: years = np.array([2005, 2005, 2006, 2005, 2006]) In [20]: df['data1'].groupby([states, years]).mean() Out[20]: California 2005 0.478943 2006 -0.519439 Ohio 2005 -0.380219 2006 1.965781 Name: data1, dtype: float64 ``` 通常,分組信息就位于相同的要處理DataFrame中。這里,你還可以將列名(可以是字符串、數字或其他Python對象)用作分組鍵: ```python In [21]: df.groupby('key1').mean() Out[21]: data1 data2 key1 a 0.746672 0.910916 b -0.537585 0.525384 In [22]: df.groupby(['key1', 'key2']).mean() Out[22]: data1 data2 key1 key2 a one 0.880536 1.319920 two 0.478943 0.092908 b one -0.519439 0.281746 two -0.555730 0.769023 ``` 你可能已經注意到了,第一個例子在執行df.groupby('key1').mean()時,結果中沒有key2列。這是因為df['key2']不是數值數據(俗稱“麻煩列”),所以被從結果中排除了。默認情況下,所有數值列都會被聚合,雖然有時可能會被過濾為一個子集,稍后就會碰到。 無論你準備拿groupby做什么,都有可能會用到GroupBy的size方法,它可以返回一個含有分組大小的Series: ```python In [23]: df.groupby(['key1', 'key2']).size() Out[23]: key1 key2 a one 2 two 1 b one 1 two 1 dtype: int64 ``` 注意,任何分組關鍵詞中的缺失值,都會被從結果中除去。 ## 對分組進行迭代 GroupBy對象支持迭代,可以產生一組二元元組(由分組名和數據塊組成)。看下面的例子: ```python In [24]: for name, group in df.groupby('key1'): ....: print(name) ....: print(group) ....: a data1 data2 key1 key2 0 -0.204708 1.393406 a one 1 0.478943 0.092908 a two 4 1.965781 1.246435 a one b data1 data2 key1 key2 2 -0.519439 0.281746 b one 3 -0.555730 0.769023 b two ``` 對于多重鍵的情況,元組的第一個元素將會是由鍵值組成的元組: ```python In [25]: for (k1, k2), group in df.groupby(['key1', 'key2']): ....: print((k1, k2)) ....: print(group) ....: ('a', 'one') data1 data2 key1 key2 0 -0.204708 1.393406 a one 4 1.965781 1.246435 a one ('a', 'two') data1 data2 key1 key2 1 0.478943 0.092908 a two ('b', 'one') data1 data2 key1 key2 2 -0.519439 0.281746 b one ('b', 'two') data1 data2 key1 key2 3 -0.55573 0.769023 b two ``` 當然,你可以對這些數據片段做任何操作。有一個你可能會覺得有用的運算:將這些數據片段做成一個字典: ```python In [26]: pieces = dict(list(df.groupby('key1'))) In [27]: pieces['b'] Out[27]: data1 data2 key1 key2 2 -0.519439 0.281746 b one 3 -0.555730 0.769023 b two ``` groupby默認是在axis=0上進行分組的,通過設置也可以在其他任何軸上進行分組。拿上面例子中的df來說,我們可以根據dtype對列進行分組: ```python In [28]: df.dtypes Out[28]: data1 float64 data2 float64 key1 object key2 object dtype: object In [29]: grouped = df.groupby(df.dtypes, axis=1) ``` 可以如下打印分組: ```python In [30]: for dtype, group in grouped: ....: print(dtype) ....: print(group) ....: float64 data1 data2 0 -0.204708 1.393406 1 0.478943 0.092908 2 -0.519439 0.281746 3 -0.555730 0.769023 4 1.965781 1.246435 object key1 key2 0 a one 1 a two 2 b one 3 b two 4 a one ``` ## 選取一列或列的子集 對于由DataFrame產生的GroupBy對象,如果用一個(單個字符串)或一組(字符串數組)列名對其進行索引,就能實現選取部分列進行聚合的目的。也就是說: ```python df.groupby('key1')['data1'] df.groupby('key1')[['data2']] ``` 是以下代碼的語法糖: ```python df['data1'].groupby(df['key1']) df[['data2']].groupby(df['key1']) ``` 尤其對于大數據集,很可能只需要對部分列進行聚合。例如,在前面那個數據集中,如果只需計算data2列的平均值并以DataFrame形式得到結果,可以這樣寫: ```python In [31]: df.groupby(['key1', 'key2'])[['data2']].mean() Out[31]: data2 key1 key2 a one 1.319920 two 0.092908 b one 0.281746 two 0.769023 ``` 這種索引操作所返回的對象是一個已分組的DataFrame(如果傳入的是列表或數組)或已分組的Series(如果傳入的是標量形式的單個列名): ```python In [32]: s_grouped = df.groupby(['key1', 'key2'])['data2'] In [33]: s_grouped Out[33]: <pandas.core.groupby.SeriesGroupBy object at 0x7faa30c78da0> In [34]: s_grouped.mean() Out[34]: key1 key2 a one 1.319920 two 0.092908 b one 0.281746 two 0.769023 Name: data2, dtype: float64 ``` ##通過字典或Series進行分組 除數組以外,分組信息還可以其他形式存在。來看另一個示例DataFrame: ```python In [35]: people = pd.DataFrame(np.random.randn(5, 5), ....: columns=['a', 'b', 'c', 'd', 'e'], ....: index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis']) In [36]: people.iloc[2:3, [1, 2]] = np.nan # Add a few NA values In [37]: people Out[37]: a b c d e Joe 1.007189 -1.296221 0.274992 0.228913 1.352917 Steve 0.886429 -2.001637 -0.371843 1.669025 -0.438570 Wes -0.539741 NaN NaN -1.021228 -0.577087 Jim 0.124121 0.302614 0.523772 0.000940 1.343810 Travis -0.713544 -0.831154 -2.370232 -1.860761 -0.860757 ``` 現在,假設已知列的分組關系,并希望根據分組計算列的和: ```python In [38]: mapping = {'a': 'red', 'b': 'red', 'c': 'blue', ....: 'd': 'blue', 'e': 'red', 'f' : 'orange'} ``` 現在,你可以將這個字典傳給groupby,來構造數組,但我們可以直接傳遞字典(我包含了鍵“f”來強調,存在未使用的分組鍵是可以的): ```python In [39]: by_column = people.groupby(mapping, axis=1) In [40]: by_column.sum() Out[40]: blue red Joe 0.503905 1.063885 Steve 1.297183 -1.553778 Wes -1.021228 -1.116829 Jim 0.524712 1.770545 Travis -4.230992 -2.405455 ``` Series也有同樣的功能,它可以被看做一個固定大小的映射: ```python In [41]: map_series = pd.Series(mapping) In [42]: map_series Out[42]: a red b red c blue d blue e red f orange dtype: object In [43]: people.groupby(map_series, axis=1).count() Out[43]: blue red Joe 2 3 Steve 2 3 Wes 1 2 Jim 2 3 Travis 2 3 ``` ##通過函數進行分組 比起使用字典或Series,使用Python函數是一種更原生的方法定義分組映射。任何被當做分組鍵的函數都會在各個索引值上被調用一次,其返回值就會被用作分組名稱。具體點說,以上一小節的示例DataFrame為例,其索引值為人的名字。你可以計算一個字符串長度的數組,更簡單的方法是傳入len函數: ```python In [44]: people.groupby(len).sum() Out[44]: a b c d e 3 0.591569 -0.993608 0.798764 -0.791374 2.119639 5 0.886429 -2.001637 -0.371843 1.669025 -0.438570 6 -0.713544 -0.831154 -2.370232 -1.860761 -0.860757 ``` 將函數跟數組、列表、字典、Series混合使用也不是問題,因為任何東西在內部都會被轉換為數組: ```python In [45]: key_list = ['one', 'one', 'one', 'two', 'two'] In [46]: people.groupby([len, key_list]).min() Out[46]: a b c d e 3 one -0.539741 -1.296221 0.274992 -1.021228 -0.577087 two 0.124121 0.302614 0.523772 0.000940 1.343810 5 one 0.886429 -2.001637 -0.371843 1.669025 -0.438570 6 two -0.713544 -0.831154 -2.370232 -1.860761 -0.860757 ``` ## 根據索引級別分組 層次化索引數據集最方便的地方就在于它能夠根據軸索引的一個級別進行聚合: ```python In [47]: columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'], ....: [1, 3, 5, 1, 3]], ....: names=['cty', 'tenor']) In [48]: hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns) In [49]: hier_df Out[49]: cty US JP tenor 1 3 5 1 3 0 0.560145 -1.265934 0.119827 -1.063512 0.332883 1 -2.359419 -0.199543 -1.541996 -0.970736 -1.307030 2 0.286350 0.377984 -0.753887 0.331286 1.349742 3 0.069877 0.246674 -0.011862 1.004812 1.327195 ``` 要根據級別分組,使用level關鍵字傳遞級別序號或名字: ```python In [50]: hier_df.groupby(level='cty', axis=1).count() Out[50]: cty JP US 0 2 3 1 2 3 2 2 3 3 2 3 ``` # 10.2 數據聚合 聚合指的是任何能夠從數組產生標量值的數據轉換過程。之前的例子已經用過一些,比如mean、count、min以及sum等。你可能想知道在GroupBy對象上調用mean()時究竟發生了什么。許多常見的聚合運算(如表10-1所示)都有進行優化。然而,除了這些方法,你還可以使用其它的。 ![表10-1 經過優化的groupby方法](https://img.kancloud.cn/b1/89/b1893c8875821faa1e280ea7ec0a212a_554x229.png) 你可以使用自己發明的聚合運算,還可以調用分組對象上已經定義好的任何方法。例如,quantile可以計算Series或DataFrame列的樣本分位數。 雖然quantile并沒有明確地實現于GroupBy,但它是一個Series方法,所以這里是能用的。實際上,GroupBy會高效地對Series進行切片,然后對各片調用piece.quantile(0.9),最后將這些結果組裝成最終結果: ```python In [51]: df Out[51]: data1 data2 key1 key2 0 -0.204708 1.393406 a one 1 0.478943 0.092908 a two 2 -0.519439 0.281746 b one 3 -0.555730 0.769023 b two 4 1.965781 1.246435 a one In [52]: grouped = df.groupby('key1') In [53]: grouped['data1'].quantile(0.9) Out[53]: key1 a 1.668413 b -0.523068 Name: data1, dtype: float64 ``` 如果要使用你自己的聚合函數,只需將其傳入aggregate或agg方法即可: ```python In [54]: def peak_to_peak(arr): ....: return arr.max() - arr.min() In [55]: grouped.agg(peak_to_peak) Out[55]: data1 data2 key1 a 2.170488 1.300498 b 0.036292 0.487276 ``` 你可能注意到注意,有些方法(如describe)也是可以用在這里的,即使嚴格來講,它們并非聚合運算: ```python In [56]: grouped.describe() Out[56]: data1 \ count mean std min 25% 50% 75% key1 a 3.0 0.746672 1.109736 -0.204708 0.137118 0.478943 1.222362 b 2.0 -0.537585 0.025662 -0.555730 -0.546657 -0.537585 -0.528512 data2 \ max count mean std min 25% 50% key1 a 1.965781 3.0 0.910916 0.712217 0.092908 0.669671 1.246435 b -0.519439 2.0 0.525384 0.344556 0.281746 0.403565 0.525384 75% max key1 a 1.319920 1.393406 b 0.647203 0.769023 ``` 在后面的10.3節,我將詳細說明這到底是怎么回事。 >筆記:自定義聚合函數要比表10-1中那些經過優化的函數慢得多。這是因為在構造中間分組數據塊時存在非常大的開銷(函數調用、數據重排等)。 ## 面向列的多函數應用 回到前面小費的例子。使用read_csv導入數據之后,我們添加了一個小費百分比的列tip_pct: ```python In [57]: tips = pd.read_csv('examples/tips.csv') # Add tip percentage of total bill In [58]: tips['tip_pct'] = tips['tip'] / tips['total_bill'] In [59]: tips[:6] Out[59]: total_bill tip smoker day time size tip_pct 0 16.99 1.01 No Sun Dinner 2 0.059447 1 10.34 1.66 No Sun Dinner 3 0.160542 2 21.01 3.50 No Sun Dinner 3 0.166587 3 23.68 3.31 No Sun Dinner 2 0.139780 4 24.59 3.61 No Sun Dinner 4 0.146808 5 25.29 4.71 No Sun Dinner 4 0.186240 ``` 你已經看到,對Series或DataFrame列的聚合運算其實就是使用aggregate(使用自定義函數)或調用諸如mean、std之類的方法。然而,你可能希望對不同的列使用不同的聚合函數,或一次應用多個函數。其實這也好辦,我將通過一些示例來進行講解。首先,我根據天和smoker對tips進行分組: ```python In [60]: grouped = tips.groupby(['day', 'smoker']) ``` 注意,對于表10-1中的那些描述統計,可以將函數名以字符串的形式傳入: ```python In [61]: grouped_pct = grouped['tip_pct'] In [62]: grouped_pct.agg('mean') Out[62]: day smoker Fri No 0.151650 Yes 0.174783 Sat No 0.158048 Yes 0.147906 Sun No 0.160113 Yes 0.187250 Thur No 0.160298 Yes 0.163863 Name: tip_pct, dtype: float64 ``` 如果傳入一組函數或函數名,得到的DataFrame的列就會以相應的函數命名: ```python In [63]: grouped_pct.agg(['mean', 'std', peak_to_peak]) Out[63]: mean std peak_to_peak day smoker Fri No 0.151650 0.028123 0.067349 Yes 0.174783 0.051293 0.159925 Sat No 0.158048 0.039767 0.235193 Yes 0.147906 0.061375 0.290095 Sun No 0.160113 0.042347 0.193226 Yes 0.187250 0.154134 0.644685 Thur No 0.160298 0.038774 0.193350 Yes 0.163863 0.039389 0.151240 ``` 這里,我們傳遞了一組聚合函數進行聚合,獨立對數據分組進行評估。 你并非一定要接受GroupBy自動給出的那些列名,特別是lambda函數,它們的名稱是'<lambda>',這樣的辨識度就很低了(通過函數的__name__屬性看看就知道了)。因此,如果傳入的是一個由(name,function)元組組成的列表,則各元組的第一個元素就會被用作DataFrame的列名(可以將這種二元元組列表看做一個有序映射): ```python In [64]: grouped_pct.agg([('foo', 'mean'), ('bar', np.std)]) Out[64]: foo bar day smoker Fri No 0.151650 0.028123 Yes 0.174783 0.051293 Sat No 0.158048 0.039767 Yes 0.147906 0.061375 Sun No 0.160113 0.042347 Yes 0.187250 0.154134 Thur No 0.160298 0.038774 Yes 0.163863 0.039389 ``` 對于DataFrame,你還有更多選擇,你可以定義一組應用于全部列的一組函數,或不同的列應用不同的函數。假設我們想要對tip_pct和total_bill列計算三個統計信息: ```python In [65]: functions = ['count', 'mean', 'max'] In [66]: result = grouped['tip_pct', 'total_bill'].agg(functions) In [67]: result Out[67]: tip_pct total_bill count mean max count mean max day smoker Fri No 4 0.151650 0.187735 4 18.420000 22.75 Yes 15 0.174783 0.263480 15 16.813333 40.17 Sat No 45 0.158048 0.291990 45 19.661778 48.33 Yes 42 0.147906 0.325733 42 21.276667 50.81 Sun No 57 0.160113 0.252672 57 20.506667 48.17 Yes 19 0.187250 0.710345 19 24.120000 45.35 Thur No 45 0.160298 0.266312 45 17.113111 41.19 Yes 17 0.163863 0.241255 17 19.190588 43.11 ``` 如你所見,結果DataFrame擁有層次化的列,這相當于分別對各列進行聚合,然后用concat將結果組裝到一起,使用列名用作keys參數: ```python In [68]: result['tip_pct'] Out[68]: count mean max day smoker Fri No 4 0.151650 0.187735 Yes 15 0.174783 0.263480 Sat No 45 0.158048 0.291990 Yes 42 0.147906 0.325733 Sun No 57 0.160113 0.252672 Yes 19 0.187250 0.710345 Thur No 45 0.160298 0.266312 Yes 17 0.163863 0.241255 ``` 跟前面一樣,這里也可以傳入帶有自定義名稱的一組元組: ```python In [69]: ftuples = [('Durchschnitt', 'mean'),('Abweichung', np.var)] In [70]: grouped['tip_pct', 'total_bill'].agg(ftuples) Out[70]: tip_pct total_bill Durchschnitt Abweichung Durchschnitt Abweichung day smoker Fri No 0.151650 0.000791 18.420000 25.596333 Yes 0.174783 0.002631 16.813333 82.562438 Sat No 0.158048 0.001581 19.661778 79.908965 Yes 0.147906 0.003767 21.276667 101.387535 Sun No 0.160113 0.001793 20.506667 66.099980 Yes 0.187250 0.023757 24.120000 109.046044 Thur No 0.160298 0.001503 17.113111 59.625081 Yes 0.163863 0.001551 19.190588 69.808518 ``` 現在,假設你想要對一個列或不同的列應用不同的函數。具體的辦法是向agg傳入一個從列名映射到函數的字典: ```python In [71]: grouped.agg({'tip' : np.max, 'size' : 'sum'}) Out[71]: tip size day smoker Fri No 3.50 9 Yes 4.73 31 Sat No 9.00 115 Yes 10.00 104 Sun No 6.00 167 Yes 6.50 49 Thur No 6.70 112 Yes 5.00 40 In [72]: grouped.agg({'tip_pct' : ['min', 'max', 'mean', 'std'], ....: 'size' : 'sum'}) Out[72]: tip_pct size min max mean std sum day smoker Fri No 0.120385 0.187735 0.151650 0.028123 9 Yes 0.103555 0.263480 0.174783 0.051293 31 Sat No 0.056797 0.291990 0.158048 0.039767 115 Yes 0.035638 0.325733 0.147906 0.061375 104 Sun No 0.059447 0.252672 0.160113 0.042347 167 Yes 0.065660 0.710345 0.187250 0.154134 49 Thur No 0.072961 0.266312 0.160298 0.038774 112 Yes 0.090014 0.241255 0.163863 0.039389 40 ``` 只有將多個函數應用到至少一列時,DataFrame才會擁有層次化的列。 ## 以“沒有行索引”的形式返回聚合數據 到目前為止,所有示例中的聚合數據都有由唯一的分組鍵組成的索引(可能還是層次化的)。由于并不總是需要如此,所以你可以向groupby傳入as_index=False以禁用該功能: ```python In [73]: tips.groupby(['day', 'smoker'], as_index=False).mean() Out[73]: day smoker total_bill tip size tip_pct 0 Fri No 18.420000 2.812500 2.250000 0.151650 1 Fri Yes 16.813333 2.714000 2.066667 0.174783 2 Sat No 19.661778 3.102889 2.555556 0.158048 3 Sat Yes 21.276667 2.875476 2.476190 0.147906 4 Sun No 20.506667 3.167895 2.929825 0.160113 5 Sun Yes 24.120000 3.516842 2.578947 0.187250 6 Thur No 17.113111 2.673778 2.488889 0.160298 7 Thur Yes 19.190588 3.030000 2.352941 0.163863 ``` 當然,對結果調用reset_index也能得到這種形式的結果。使用as_index=False方法可以避免一些不必要的計算。 # 10.3 apply:一般性的“拆分-應用-合并” 最通用的GroupBy方法是apply,本節剩余部分將重點講解它。如圖10-2所示,apply會將待處理的對象拆分成多個片段,然后對各片段調用傳入的函數,最后嘗試將各片段組合到一起。 ![圖10-2 分組聚合示例](https://img.kancloud.cn/24/28/2428dc9ee8d50be4b266882d555feddd_1038x896.png) 回到之前那個小費數據集,假設你想要根據分組選出最高的5個tip_pct值。首先,編寫一個選取指定列具有最大值的行的函數: ```python In [74]: def top(df, n=5, column='tip_pct'): ....: return df.sort_values(by=column)[-n:] In [75]: top(tips, n=6) Out[75]: total_bill tip smoker day time size tip_pct 109 14.31 4.00 Yes Sat Dinner 2 0.279525 183 23.17 6.50 Yes Sun Dinner 4 0.280535 232 11.61 3.39 No Sat Dinner 2 0.291990 67 3.07 1.00 Yes Sat Dinner 1 0.325733 178 9.60 4.00 Yes Sun Dinner 2 0.416667 172 7.25 5.15 Yes Sun Dinner 2 0.710345 ``` 現在,如果對smoker分組并用該函數調用apply,就會得到: ```python In [76]: tips.groupby('smoker').apply(top) Out[76]: total_bill tip smoker day time size tip_pct smoker No 88 24.71 5.85 No Thur Lunch 2 0.236746 185 20.69 5.00 No Sun Dinner 5 0.241663 51 10.29 2.60 No Sun Dinner 2 0.252672 149 7.51 2.00 No Thur Lunch 2 0.266312 232 11.61 3.39 No Sat Dinner 2 0.291990 Yes 109 14.31 4.00 Yes Sat Dinner 2 0.279525 183 23.17 6.50 Yes Sun Dinner 4 0.280535 67 3.07 1.00 Yes Sat Dinner 1 0.325733 178 9.60 4.00 Yes Sun Dinner 2 0.416667 172 7.25 5.15 Yes Sun Dinner 2 0.710345 ``` 這里發生了什么?top函數在DataFrame的各個片段上調用,然后結果由pandas.concat組裝到一起,并以分組名稱進行了標記。于是,最終結果就有了一個層次化索引,其內層索引值來自原DataFrame。 如果傳給apply的函數能夠接受其他參數或關鍵字,則可以將這些內容放在函數名后面一并傳入: ```python In [77]: tips.groupby(['smoker', 'day']).apply(top, n=1, column='total_bill') Out[77]: total_bill tip smoker day time size tip_pct smoker day No Fri 94 22.75 3.25 No Fri Dinner 2 0.142857 Sat 212 48.33 9.00 No Sat Dinner 4 0.186220 Sun 156 48.17 5.00 No Sun Dinner 6 0.103799 Thur 142 41.19 5.00 No Thur Lunch 5 0.121389 Yes Fri 95 40.17 4.73 Yes Fri Dinner 4 0.117750 Sat 170 50.81 10.00 Yes Sat Dinner 3 0.196812 Sun 182 45.35 3.50 Yes Sun Dinner 3 0.077178 Thur 197 43.11 5.00 Yes Thur Lunch 4 0.115982 ``` >筆記:除這些基本用法之外,能否充分發揮apply的威力很大程度上取決于你的創造力。傳入的那個函數能做什么全由你說了算,它只需返回一個pandas對象或標量值即可。本章后續部分的示例主要用于講解如何利用groupby解決各種各樣的問題。 可能你已經想起來了,之前我在GroupBy對象上調用過describe: ```python In [78]: result = tips.groupby('smoker')['tip_pct'].describe() In [79]: result Out[79]: count mean std min 25% 50% 75% \ smoker No 151.0 0.159328 0.039910 0.056797 0.136906 0.155625 0.185014 Yes 93.0 0.163196 0.085119 0.035638 0.106771 0.153846 0.195059 max smoker No 0.291990 Yes 0.710345 In [80]: result.unstack('smoker') Out[80]: smoker count No 151.000000 Yes 93.000000 mean No 0.159328 Yes 0.163196 std No 0.039910 Yes 0.085119 min No 0.056797 Yes 0.035638 25% No 0.136906 Yes 0.106771 50% No 0.155625 Yes 0.153846 75% No 0.185014 Yes 0.195059 max No 0.291990 Yes 0.710345 dtype: float64 ``` 在GroupBy中,當你調用諸如describe之類的方法時,實際上只是應用了下面兩條代碼的快捷方式而已: ```python f = lambda x: x.describe() grouped.apply(f) ``` ## 禁止分組鍵 從上面的例子中可以看出,分組鍵會跟原始對象的索引共同構成結果對象中的層次化索引。將group_keys=False傳入groupby即可禁止該效果: ```python In [81]: tips.groupby('smoker', group_keys=False).apply(top) Out[81]: total_bill tip smoker day time size tip_pct 88 24.71 5.85 No Thur Lunch 2 0.236746 185 20.69 5.00 No Sun Dinner 5 0.241663 51 10.29 2.60 No Sun Dinner 2 0.252672 149 7.51 2.00 No Thur Lunch 2 0.266312 232 11.61 3.39 No Sat Dinner 2 0.291990 109 14.31 4.00 Yes Sat Dinner 2 0.279525 183 23.17 6.50 Yes Sun Dinner 4 0.280535 67 3.07 1.00 Yes Sat Dinner 1 0.325733 178 9.60 4.00 Yes Sun Dinner 2 0.416667 172 7.25 5.15 Yes Sun Dinner 2 0.710345 ``` ## 分位數和桶分析 我曾在第8章中講過,pandas有一些能根據指定面元或樣本分位數將數據拆分成多塊的工具(比如cut和qcut)。將這些函數跟groupby結合起來,就能非常輕松地實現對數據集的桶(bucket)或分位數(quantile)分析了。以下面這個簡單的隨機數據集為例,我們利用cut將其裝入長度相等的桶中: ```python In [82]: frame = pd.DataFrame({'data1': np.random.randn(1000), ....: 'data2': np.random.randn(1000)}) In [83]: quartiles = pd.cut(frame.data1, 4) In [84]: quartiles[:10] Out[84]: 0 (-1.23, 0.489] 1 (-2.956, -1.23] 2 (-1.23, 0.489] 3 (0.489, 2.208] 4 (-1.23, 0.489] 5 (0.489, 2.208] 6 (-1.23, 0.489] 7 (-1.23, 0.489] 8 (0.489, 2.208] 9 (0.489, 2.208] Name: data1, dtype: category Categories (4, interval[float64]): [(-2.956, -1.23] < (-1.23, 0.489] < (0.489, 2. 208] < (2.208, 3.928]] ``` 由cut返回的Categorical對象可直接傳遞到groupby。因此,我們可以像下面這樣對data2列做一些統計計算: ```python In [85]: def get_stats(group): ....: return {'min': group.min(), 'max': group.max(), ....: 'count': group.count(), 'mean': group.mean()} In [86]: grouped = frame.data2.groupby(quartiles) In [87]: grouped.apply(get_stats).unstack() Out[87]: count max mean min data1 (-2.956, -1.23] 95.0 1.670835 -0.039521 -3.399312 (-1.23, 0.489] 598.0 3.260383 -0.002051 -2.989741 (0.489, 2.208] 297.0 2.954439 0.081822 -3.745356 (2.208, 3.928] 10.0 1.765640 0.024750 -1.929776 ``` 這些都是長度相等的桶。要根據樣本分位數得到大小相等的桶,使用qcut即可。傳入labels=False即可只獲取分位數的編號: ```python # Return quantile numbers In [88]: grouping = pd.qcut(frame.data1, 10, labels=False) In [89]: grouped = frame.data2.groupby(grouping) In [90]: grouped.apply(get_stats).unstack() Out[90]: count max mean min data1 0 100.0 1.670835 -0.049902 -3.399312 1 100.0 2.628441 0.030989 -1.950098 2 100.0 2.527939 -0.067179 -2.925113 3 100.0 3.260383 0.065713 -2.315555 4 100.0 2.074345 -0.111653 -2.047939 5 100.0 2.184810 0.052130 -2.989741 6 100.0 2.458842 -0.021489 -2.223506 7 100.0 2.954439 -0.026459 -3.056990 8 100.0 2.735527 0.103406 -3.745356 9 100.0 2.377020 0.220122 -2.064111 ``` 我們會在第12章詳細講解pandas的Categorical類型。 ## 示例:用特定于分組的值填充缺失值 對于缺失數據的清理工作,有時你會用dropna將其替換掉,而有時則可能會希望用一個固定值或由數據集本身所衍生出來的值去填充NA值。這時就得使用fillna這個工具了。在下面這個例子中,我用平均值去填充NA值: ```python In [91]: s = pd.Series(np.random.randn(6)) In [92]: s[::2] = np.nan In [93]: s Out[93]: 0 NaN 1 -0.125921 2 NaN 3 -0.884475 4 NaN 5 0.227290 dtype: float64 In [94]: s.fillna(s.mean()) Out[94]: 0 -0.261035 1 -0.125921 2 -0.261035 3 -0.884475 4 -0.261035 5 0.227290 dtype: float64 ``` 假設你需要對不同的分組填充不同的值。一種方法是將數據分組,并使用apply和一個能夠對各數據塊調用fillna的函數即可。下面是一些有關美國幾個州的示例數據,這些州又被分為東部和西部: ```python In [95]: states = ['Ohio', 'New York', 'Vermont', 'Florida', ....: 'Oregon', 'Nevada', 'California', 'Idaho'] In [96]: group_key = ['East'] * 4 + ['West'] * 4 In [97]: data = pd.Series(np.random.randn(8), index=states) In [98]: data Out[98]: Ohio 0.922264 New York -2.153545 Vermont -0.365757 Florida -0.375842 Oregon 0.329939 Nevada 0.981994 California 1.105913 Idaho -1.613716 dtype: float64 ``` ['East'] * 4產生了一個列表,包括了['East']中元素的四個拷貝。將這些列表串聯起來。 將一些值設為缺失: ```python In [99]: data[['Vermont', 'Nevada', 'Idaho']] = np.nan In [100]: data Out[100]: Ohio 0.922264 New York -2.153545 Vermont NaN Florida -0.375842 Oregon 0.329939 Nevada NaN California 1.105913 Idaho NaN dtype: float64 In [101]: data.groupby(group_key).mean() Out[101]: East -0.535707 West 0.717926 dtype: float64 ``` 我們可以用分組平均值去填充NA值: ```python In [102]: fill_mean = lambda g: g.fillna(g.mean()) In [103]: data.groupby(group_key).apply(fill_mean) Out[103]: Ohio 0.922264 New York -2.153545 Vermont -0.535707 Florida -0.375842 Oregon 0.329939 Nevada 0.717926 California 1.105913 Idaho 0.717926 dtype: float64 ``` 另外,也可以在代碼中預定義各組的填充值。由于分組具有一個name屬性,所以我們可以拿來用一下: ```python In [104]: fill_values = {'East': 0.5, 'West': -1} In [105]: fill_func = lambda g: g.fillna(fill_values[g.name]) In [106]: data.groupby(group_key).apply(fill_func) Out[106]: Ohio 0.922264 New York -2.153545 Vermont 0.500000 Florida -0.375842 Oregon 0.329939 Nevada -1.000000 California 1.105913 Idaho -1.000000 dtype: float64 ``` ## 示例:隨機采樣和排列 假設你想要從一個大數據集中隨機抽取(進行替換或不替換)樣本以進行蒙特卡羅模擬(Monte Carlo simulation)或其他分析工作。“抽取”的方式有很多,這里使用的方法是對Series使用sample方法: ```python # Hearts, Spades, Clubs, Diamonds suits = ['H', 'S', 'C', 'D'] card_val = (list(range(1, 11)) + [10] * 3) * 4 base_names = ['A'] + list(range(2, 11)) + ['J', 'K', 'Q'] cards = [] for suit in ['H', 'S', 'C', 'D']: cards.extend(str(num) + suit for num in base_names) deck = pd.Series(card_val, index=cards) ``` 現在我有了一個長度為52的Series,其索引包括牌名,值則是21點或其他游戲中用于計分的點數(為了簡單起見,我當A的點數為1): ```python In [108]: deck[:13] Out[108]: AH 1 2H 2 3H 3 4H 4 5H 5 6H 6 7H 7 8H 8 9H 9 10H 10 JH 10 KH 10 QH 10 dtype: int64 ``` 現在,根據我上面所講的,從整副牌中抽出5張,代碼如下: ```python In [109]: def draw(deck, n=5): .....: return deck.sample(n) In [110]: draw(deck) Out[110]: AD 1 8C 8 5H 5 KC 10 2C 2 dtype: int64 ``` 假設你想要從每種花色中隨機抽取兩張牌。由于花色是牌名的最后一個字符,所以我們可以據此進行分組,并使用apply: ```python In [111]: get_suit = lambda card: card[-1] # last letter is suit In [112]: deck.groupby(get_suit).apply(draw, n=2) Out[112]: C 2C 2 3C 3 D KD 10 8D 8 H KH 10 3H 3 S 2S 2 4S 4 dtype: int64 ``` 或者,也可以這樣寫: ```python In [113]: deck.groupby(get_suit, group_keys=False).apply(draw, n=2) Out[113]: KC 10 JC 10 AD 1 5D 5 5H 5 6H 6 7S 7 KS 10 dtype: int64 ``` ## 示例:分組加權平均數和相關系數 根據groupby的“拆分-應用-合并”范式,可以進行DataFrame的列與列之間或兩個Series之間的運算(比如分組加權平均)。以下面這個數據集為例,它含有分組鍵、值以及一些權重值: ```python In [114]: df = pd.DataFrame({'category': ['a', 'a', 'a', 'a', .....: 'b', 'b', 'b', 'b'], .....: 'data': np.random.randn(8), .....: 'weights': np.random.rand(8)}) In [115]: df Out[115]: category data weights 0 a 1.561587 0.957515 1 a 1.219984 0.347267 2 a -0.482239 0.581362 3 a 0.315667 0.217091 4 b -0.047852 0.894406 5 b -0.454145 0.918564 6 b -0.556774 0.277825 7 b 0.253321 0.955905 ``` 然后可以利用category計算分組加權平均數: ```python In [116]: grouped = df.groupby('category') In [117]: get_wavg = lambda g: np.average(g['data'], weights=g['weights']) In [118]: grouped.apply(get_wavg) Out[118]: category a 0.811643 b -0.122262 dtype: float64 ``` 另一個例子,考慮一個來自Yahoo!Finance的數據集,其中含有幾只股票和標準普爾500指數(符號SPX)的收盤價: ```python In [119]: close_px = pd.read_csv('examples/stock_px_2.csv', parse_dates=True, .....: index_col=0) In [120]: close_px.info() <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2214 entries, 2003-01-02 to 2011-10-14 Data columns (total 4 columns): AAPL 2214 non-null float64 MSFT 2214 non-null float64 XOM 2214 non-null float64 SPX 2214 non-null float64 dtypes: float64(4) memory usage: 86.5 KB In [121]: close_px[-4:] Out[121]: AAPL MSFT XOM SPX 2011-10-11 400.29 27.00 76.27 1195.54 2011-10-12 402.19 26.96 77.16 1207.25 2011-10-13 408.43 27.18 76.37 1203.66 2011-10-14 422.00 27.27 78.11 1224.58 ``` 來做一個比較有趣的任務:計算一個由日收益率(通過百分數變化計算)與SPX之間的年度相關系數組成的DataFrame。下面是一個實現辦法,我們先創建一個函數,用它計算每列和SPX列的成對相關系數: ```python In [122]: spx_corr = lambda x: x.corrwith(x['SPX']) ``` 接下來,我們使用pct_change計算close_px的百分比變化: ```python In [123]: rets = close_px.pct_change().dropna() ``` 最后,我們用年對百分比變化進行分組,可以用一個一行的函數,從每行的標簽返回每個datetime標簽的year屬性: ```python In [124]: get_year = lambda x: x.year In [125]: by_year = rets.groupby(get_year) In [126]: by_year.apply(spx_corr) Out[126]: AAPL MSFT XOM SPX 2003 0.541124 0.745174 0.661265 1.0 2004 0.374283 0.588531 0.557742 1.0 2005 0.467540 0.562374 0.631010 1.0 2006 0.428267 0.406126 0.518514 1.0 2007 0.508118 0.658770 0.786264 1.0 2008 0.681434 0.804626 0.828303 1.0 2009 0.707103 0.654902 0.797921 1.0 2010 0.710105 0.730118 0.839057 1.0 2011 0.691931 0.800996 0.859975 1.0 ``` 當然,你還可以計算列與列之間的相關系數。這里,我們計算Apple和Microsoft的年相關系數: ```python In [127]: by_year.apply(lambda g: g['AAPL'].corr(g['MSFT'])) Out[127]: 2003 0.480868 2004 0.259024 2005 0.300093 2006 0.161735 2007 0.417738 2008 0.611901 2009 0.432738 2010 0.571946 2011 0.581987 dtype: float64 ``` ## 示例:組級別的線性回歸 順著上一個例子繼續,你可以用groupby執行更為復雜的分組統計分析,只要函數返回的是pandas對象或標量值即可。例如,我可以定義下面這個regress函數(利用statsmodels計量經濟學庫)對各數據塊執行普通最小二乘法(Ordinary Least Squares,OLS)回歸: ```python import statsmodels.api as sm def regress(data, yvar, xvars): Y = data[yvar] X = data[xvars] X['intercept'] = 1. result = sm.OLS(Y, X).fit() return result.params ``` 現在,為了按年計算AAPL對SPX收益率的線性回歸,執行: ```python In [129]: by_year.apply(regress, 'AAPL', ['SPX']) Out[129]: SPX intercept 2003 1.195406 0.000710 2004 1.363463 0.004201 2005 1.766415 0.003246 2006 1.645496 0.000080 2007 1.198761 0.003438 2008 0.968016 -0.001110 2009 0.879103 0.002954 2010 1.052608 0.001261 2011 0.806605 0.001514 ``` # 10.4 透視表和交叉表 透視表(pivot table)是各種電子表格程序和其他數據分析軟件中一種常見的數據匯總工具。它根據一個或多個鍵對數據進行聚合,并根據行和列上的分組鍵將數據分配到各個矩形區域中。在Python和pandas中,可以通過本章所介紹的groupby功能以及(能夠利用層次化索引的)重塑運算制作透視表。DataFrame有一個pivot_table方法,此外還有一個頂級的pandas.pivot_table函數。除能為groupby提供便利之外,pivot_table還可以添加分項小計,也叫做margins。 回到小費數據集,假設我想要根據day和smoker計算分組平均數(pivot_table的默認聚合類型),并將day和smoker放到行上: ```python In [130]: tips.pivot_table(index=['day', 'smoker']) Out[130]: size tip tip_pct total_bill day smoker Fri No 2.250000 2.812500 0.151650 18.420000 Yes 2.066667 2.714000 0.174783 16.813333 Sat No 2.555556 3.102889 0.158048 19.661778 Yes 2.476190 2.875476 0.147906 21.276667 Sun No 2.929825 3.167895 0.160113 20.506667 Yes 2.578947 3.516842 0.187250 24.120000 Thur No 2.488889 2.673778 0.160298 17.113111 Yes 2.352941 3.030000 0.163863 19.190588 ``` 可以用groupby直接來做。現在,假設我們只想聚合tip_pct和size,而且想根據time進行分組。我將smoker放到列上,把day放到行上: ```python In [131]: tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'], .....: columns='smoker') Out[131]: size tip_pct smoker No Yes No Yes time day Dinner Fri 2.000000 2.222222 0.139622 0.165347 Sat 2.555556 2.476190 0.158048 0.147906 Sun 2.929825 2.578947 0.160113 0.187250 Thur 2.000000 NaN 0.159744 NaN Lunch Fri 3.000000 1.833333 0.187735 0.188937 Thur 2.500000 2.352941 0.160311 0.163863 ``` 還可以對這個表作進一步的處理,傳入margins=True添加分項小計。這將會添加標簽為All的行和列,其值對應于單個等級中所有數據的分組統計: ```python In [132]: tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'], .....: columns='smoker', margins=True) Out[132]: size tip_pct smoker No Yes All No Yes All time day Dinner Fri 2.000000 2.222222 2.166667 0.139622 0.165347 0.158916 Sat 2.555556 2.476190 2.517241 0.158048 0.147906 0.153152 Sun 2.929825 2.578947 2.842105 0.160113 0.187250 0.166897 Thur 2.000000 NaN 2.000000 0.159744 NaN 0.159744 Lunch Fri 3.000000 1.833333 2.000000 0.187735 0.188937 0.188765 Thur 2.500000 2.352941 2.459016 0.160311 0.163863 0.161301 All 2.668874 2.408602 2.569672 0.159328 0.163196 0.160803 ``` 這里,All值為平均數:不單獨考慮煙民與非煙民(All列),不單獨考慮行分組兩個級別中的任何單項(All行)。 要使用其他的聚合函數,將其傳給aggfunc即可。例如,使用count或len可以得到有關分組大小的交叉表(計數或頻率): ```python In [133]: tips.pivot_table('tip_pct', index=['time', 'smoker'], columns='day', .....: aggfunc=len, margins=True) Out[133]: day Fri Sat Sun Thur All time smoker Dinner No 3.0 45.0 57.0 1.0 106.0 Yes 9.0 42.0 19.0 NaN 70.0 Lunch No 1.0 NaN NaN 44.0 45.0 Yes 6.0 NaN NaN 17.0 23.0 All 19.0 87.0 76.0 62.0 244.0 ``` 如果存在空的組合(也就是NA),你可能會希望設置一個fill_value: ```python In [134]: tips.pivot_table('tip_pct', index=['time', 'size', 'smoker'], .....: columns='day', aggfunc='mean', fill_value=0) Out[134]: day Fri Sat Sun Thur time size smoker Dinner 1 No 0.000000 0.137931 0.000000 0.000000 Yes 0.000000 0.325733 0.000000 0.000000 2 No 0.139622 0.162705 0.168859 0.159744 Yes 0.171297 0.148668 0.207893 0.000000 3 No 0.000000 0.154661 0.152663 0.000000 Yes 0.000000 0.144995 0.152660 0.000000 4 No 0.000000 0.150096 0.148143 0.000000 Yes 0.117750 0.124515 0.193370 0.000000 5 No 0.000000 0.000000 0.206928 0.000000 Yes 0.000000 0.106572 0.065660 0.000000 ... ... ... ... ... Lunch 1 No 0.000000 0.000000 0.000000 0.181728 Yes 0.223776 0.000000 0.000000 0.000000 2 No 0.000000 0.000000 0.000000 0.166005 Yes 0.181969 0.000000 0.000000 0.158843 3 No 0.187735 0.000000 0.000000 0.084246 Yes 0.000000 0.000000 0.000000 0.204952 4 No 0.000000 0.000000 0.000000 0.138919 Yes 0.000000 0.000000 0.000000 0.155410 5 No 0.000000 0.000000 0.000000 0.121389 6 No 0.000000 0.000000 0.000000 0.173706 [21 rows x 4 columns] ``` pivot_table的參數說明請參見表10-2。 ![表10-2 pivot_table的選項](https://img.kancloud.cn/90/c0/90c0bff48d166f3205beeff503b32e5e_789x258.png) ## 交叉表:crosstab 交叉表(cross-tabulation,簡稱crosstab)是一種用于計算分組頻率的特殊透視表。看下面的例子: ```python In [138]: data Out[138]: Sample Nationality Handedness 0 1 USA Right-handed 1 2 Japan Left-handed 2 3 USA Right-handed 3 4 Japan Right-handed 4 5 Japan Left-handed 5 6 Japan Right-handed 6 7 USA Right-handed 7 8 USA Left-handed 8 9 Japan Right-handed 9 10 USA Right-handed ``` 作為調查分析的一部分,我們可能想要根據國籍和用手習慣對這段數據進行統計匯總。雖然可以用pivot_table實現該功能,但是pandas.crosstab函數會更方便: ```python In [139]: pd.crosstab(data.Nationality, data.Handedness, margins=True) Out[139]: Handedness Left-handed Right-handed All Nationality Japan 2 3 5 USA 1 4 5 All 3 7 10 ``` crosstab的前兩個參數可以是數組或Series,或是數組列表。就像小費數據: ```python In [140]: pd.crosstab([tips.time, tips.day], tips.smoker, margins=True) Out[140]: smoker No Yes All time day Dinner Fri 3 9 12 Sat 45 42 87 Sun 57 19 76 Thur 1 0 1 Lunch Fri 1 6 7 Thur 44 17 61 All 151 93 244 ``` # 10.5 總結 掌握pandas數據分組工具既有助于數據清理,也有助于建模或統計分析工作。在第14章,我們會看幾個例子,對真實數據使用groupby。 在下一章,我們將關注時間序列數據。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看