• 5.3 汇总和计算描述统计
    • 相关系数与协方差
    • 唯一值、值计数以及成员资格

    5.3 汇总和计算描述统计

    pandas对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从Series中提取单个值(如sum或mean)或从DataFrame的行或列中提取一个Series。跟对应的NumPy数组方法相比,它们都是基于没有缺失数据的假设而构建的。看一个简单的DataFrame:

    1. In [230]: df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
    2. .....: [np.nan, np.nan], [0.75, -1.3]],
    3. .....: index=['a', 'b', 'c', 'd'],
    4. .....: columns=['one', 'two'])
    5. In [231]: df
    6. Out[231]:
    7. one two
    8. a 1.40 NaN
    9. b 7.10 -4.5
    10. c NaN NaN
    11. d 0.75 -1.3

    调用DataFrame的sum方法将会返回一个含有列的和的Series:

    1. In [232]: df.sum()
    2. Out[232]:
    3. one 9.25
    4. two -5.80
    5. dtype: float64

    传入axis=’columns’或axis=1将会按行进行求和运算:

    1. In [233]: df.sum(axis=1)
    2. Out[233]:
    3. a 1.40
    4. b 2.60
    5. c NaN
    6. d -0.55

    NA值会自动被排除,除非整个切片(这里指的是行或列)都是NA。通过skipna选项可以禁用该功能:

    1. In [234]: df.mean(axis='columns', skipna=False)
    2. Out[234]:
    3. a NaN
    4. b 1.300
    5. c NaN
    6. d -0.275
    7. dtype: float64

    表5-7列出了这些约简方法的常用选项。

    5.3 汇总和计算描述统计 - 图1

    有些方法(如idxmin和idxmax)返回的是间接统计(比如达到最小值或最大值的索引):

    1. In [235]: df.idxmax()
    2. Out[235]:
    3. one b
    4. two d
    5. dtype: object

    另一些方法则是累计型的:

    1. In [236]: df.cumsum()
    2. Out[236]:
    3. one two
    4. a 1.40 NaN
    5. b 8.50 -4.5
    6. c NaN NaN
    7. d 9.25 -5.8

    还有一种方法,它既不是约简型也不是累计型。describe就是一个例子,它用于一次性产生多个汇总统计:

    1. In [237]: df.describe()
    2. Out[237]:
    3. one two
    4. count 3.000000 2.000000
    5. mean 3.083333 -2.900000
    6. std 3.493685 2.262742
    7. min 0.750000 -4.500000
    8. 25% 1.075000 -3.700000
    9. 50% 1.400000 -2.900000
    10. 75% 4.250000 -2.100000
    11. max 7.100000 -1.300000

    对于非数值型数据,describe会产生另外一种汇总统计:

    1. In [238]: obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
    2. In [239]: obj.describe()
    3. Out[239]:
    4. count 16
    5. unique 3
    6. top a
    7. freq 8
    8. dtype: object

    表5-8列出了所有与描述统计相关的方法。

    5.3 汇总和计算描述统计 - 图2

    相关系数与协方差

    有些汇总统计(如相关系数和协方差)是通过参数对计算出来的。我们来看几个DataFrame,它们的数据来自Yahoo!Finance的股票价格和成交量,使用的是pandas-datareader包(可以用conda或pip安装):

    1. conda install pandas-datareader

    我使用pandas_datareader模块下载了一些股票数据:

    1. import pandas_datareader.data as web
    2. all_data = {ticker: web.get_data_yahoo(ticker)
    3. for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
    4. price = pd.DataFrame({ticker: data['Adj Close']
    5. for ticker, data in all_data.items()})
    6. volume = pd.DataFrame({ticker: data['Volume']
    7. for ticker, data in all_data.items()})

    注意:此时Yahoo! Finance已经不存在了,因为2017年Yahoo!被Verizon收购了。参阅pandas-datareader文档,可以学习最新的功能。

    现在计算价格的百分数变化,时间序列的操作会在第11章介绍:

    1. In [242]: returns = price.pct_change()
    2. In [243]: returns.tail()
    3. Out[243]:
    4. AAPL GOOG IBM MSFT
    5. Date
    6. 2016-10-17 -0.000680 0.001837 0.002072 -0.003483
    7. 2016-10-18 -0.000681 0.019616 -0.026168 0.007690
    8. 2016-10-19 -0.002979 0.007846 0.003583 -0.002255
    9. 2016-10-20 -0.000512 -0.005652 0.001719 -0.004867
    10. 2016-10-21 -0.003930 0.003011 -0.012474 0.042096

    Series的corr方法用于计算两个Series中重叠的、非NA的、按索引对齐的值的相关系数。与此类似,cov用于计算协方差:

    1. In [244]: returns['MSFT'].corr(returns['IBM'])
    2. Out[244]: 0.49976361144151144
    3. In [245]: returns['MSFT'].cov(returns['IBM'])
    4. Out[245]: 8.8706554797035462e-05

    因为MSTF是一个合理的Python属性,我们还可以用更简洁的语法选择列:

    1. In [246]: returns.MSFT.corr(returns.IBM)
    2. Out[246]: 0.49976361144151144

    另一方面,DataFrame的corr和cov方法将以DataFrame的形式分别返回完整的相关系数或协方差矩阵:

    1. In [247]: returns.corr()
    2. Out[247]:
    3. AAPL GOOG IBM MSFT
    4. AAPL 1.000000 0.407919 0.386817 0.389695
    5. GOOG 0.407919 1.000000 0.405099 0.465919
    6. IBM 0.386817 0.405099 1.000000 0.499764
    7. MSFT 0.389695 0.465919 0.499764 1.000000
    8. In [248]: returns.cov()
    9. Out[248]:
    10. AAPL GOOG IBM MSFT
    11. AAPL 0.000277 0.000107 0.000078 0.000095
    12. GOOG 0.000107 0.000251 0.000078 0.000108
    13. IBM 0.000078 0.000078 0.000146 0.000089
    14. MSFT 0.000095 0.000108 0.000089 0.000215

    利用DataFrame的corrwith方法,你可以计算其列或行跟另一个Series或DataFrame之间的相关系数。传入一个Series将会返回一个相关系数值Series(针对各列进行计算):

    1. In [249]: returns.corrwith(returns.IBM)
    2. Out[249]:
    3. AAPL 0.386817
    4. GOOG 0.405099
    5. IBM 1.000000
    6. MSFT 0.499764
    7. dtype: float64

    传入一个DataFrame则会计算按列名配对的相关系数。这里,我计算百分比变化与成交量的相关系数:

    1. In [250]: returns.corrwith(volume)
    2. Out[250]:
    3. AAPL -0.075565
    4. GOOG -0.007067
    5. IBM -0.204849
    6. MSFT -0.092950
    7. dtype: float64

    传入axis=’columns’即可按行进行计算。无论如何,在计算相关系数之前,所有的数据项都会按标签对齐。

    唯一值、值计数以及成员资格

    还有一类方法可以从一维Series的值中抽取信息。看下面的例子:

    1. In [251]: obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])

    第一个函数是unique,它可以得到Series中的唯一值数组:

    1. In [252]: uniques = obj.unique()
    2. In [253]: uniques
    3. Out[253]: array(['c', 'a', 'd', 'b'], dtype=object)

    返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序(uniques.sort())。相似的,value_counts用于计算一个Series中各值出现的频率:

    1. In [254]: obj.value_counts()
    2. Out[254]:
    3. c 3
    4. a 3
    5. b 2
    6. d 1
    7. dtype: int64

    为了便于查看,结果Series是按值频率降序排列的。value_counts还是一个顶级pandas方法,可用于任何数组或序列:

    1. In [255]: pd.value_counts(obj.values, sort=False)
    2. Out[255]:
    3. a 3
    4. b 2
    5. c 3
    6. d 1
    7. dtype: int64

    isin用于判断矢量化集合的成员资格,可用于过滤Series中或DataFrame列中数据的子集:

    1. In [256]: obj
    2. Out[256]:
    3. 0 c
    4. 1 a
    5. 2 d
    6. 3 a
    7. 4 a
    8. 5 b
    9. 6 b
    10. 7 c
    11. 8 c
    12. dtype: object
    13. In [257]: mask = obj.isin(['b', 'c'])
    14. In [258]: mask
    15. Out[258]:
    16. 0 True
    17. 1 False
    18. 2 False
    19. 3 False
    20. 4 False
    21. 5 True
    22. 6 True
    23. 7 True
    24. 8 True
    25. dtype: bool
    26. In [259]: obj[mask]
    27. Out[259]:
    28. 0 c
    29. 5 b
    30. 6 b
    31. 7 c
    32. 8 c
    33. dtype: object

    与isin类似的是Index.get_indexer方法,它可以给你一个索引数组,从可能包含重复值的数组到另一个不同值的数组:

    1. In [260]: to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
    2. In [261]: unique_vals = pd.Series(['c', 'b', 'a'])
    3. In [262]: pd.Index(unique_vals).get_indexer(to_match)
    4. Out[262]: array([0, 2, 1, 1, 0, 2])

    表5-9给出了这几个方法的一些参考信息。

    表5-9 唯一值、值计数、成员资格方法

    有时,你可能希望得到DataFrame中多个相关列的一张柱状图。例如:

    1. In [263]: data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
    2. .....: 'Qu2': [2, 3, 1, 2, 3],
    3. .....: 'Qu3': [1, 5, 2, 4, 4]})
    4. In [264]: data
    5. Out[264]:
    6. Qu1 Qu2 Qu3
    7. 0 1 2 1
    8. 1 3 3 5
    9. 2 4 1 2
    10. 3 3 2 4
    11. 4 4 3 4

    将pandas.value_counts传给该DataFrame的apply函数,就会出现:

    1. In [265]: result = data.apply(pd.value_counts).fillna(0)
    2. In [266]: result
    3. Out[266]:
    4. Qu1 Qu2 Qu3
    5. 1 1.0 1.0 1.0
    6. 2 0.0 2.0 1.0
    7. 3 2.0 2.0 0.0
    8. 4 2.0 0.0 2.0
    9. 5 0.0 0.0 1.0

    这里,结果中的行标签是所有列的唯一值。后面的频率值是每个列中这些值的相应计数。