美文网首页数据蛙数据分析每周作业小秩学数据分析
pandas文档0.24.1第三章 马上开始(二)

pandas文档0.24.1第三章 马上开始(二)

作者: Lykit01 | 来源:发表于2019-03-18 13:20 被阅读0次

    目录:
    第一章 0.24.1版本新特性
    第二章 安装
    第三章 马上开始(一)快速上手
    第三章 马上开始(二)基础功能

    [toc]

    3.3 重要基础功能

    现在我们讨论一下pandas数据结构共有的一些基础功能。在前文的例子中我们已经创建过一些pandas数据结构,比如:

    In [1]: index = pd.date_range('1/1/2000', periods=8)
    
    In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
    
    In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index,
       ...:                   columns=['A', 'B', 'C'])
    
    In [4]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'],
       ...:               major_axis=pd.date_range('1/1/2000', periods=5),
       ...:               minor_axis=['A', 'B', 'C', 'D'])
    
    

    3.3.1 头和尾

    在pandas中我们用head()和tail()方法来观察Series和DataFrame的一小部分样本。预览操作默认能看到的样本个数是5个,读者可以按需求更改。

    In [5]: long_series = pd.Series(np.random.randn(1000))
    
    In [6]: long_series.head()
    Out[6]: 
    0   -2.211372
    1    0.974466
    2   -2.006747
    3   -0.410001
    4   -0.078638
    dtype: float64
    
    In [7]: long_series.tail(3)
    Out[7]: 
    997   -0.196166
    998    0.380733
    999   -0.275874
    dtype: float64
    

    3.3.2 属性和底层数据

    pandas对象有很多属性,能够让用户直接访问到元数据(metadata)。

    • shape:给出对象的轴维度,与NumPy的n维数组一致
    • 轴标签
      • Series:索引(index是Series唯一的轴)
      • DataFrame:索引(行)和列
      • Panel:条目(items)、主轴(major_axis)和次轴(minor_axis)
        提示:可以大胆地给这些属性赋值。
    In [8]: df[:2]
    Out[8]: 
                       A         B         C
    2000-01-01 -0.173215  0.119209 -1.044236
    2000-01-02 -0.861849 -2.104569 -0.494929
    
    In [9]: df.columns = [x.lower() for x in df.columns]
    
    In [10]: df
    Out[10]: 
                       a         b         c
    2000-01-01 -0.173215  0.119209 -1.044236
    2000-01-02 -0.861849 -2.104569 -0.494929
    2000-01-03  1.071804  0.721555 -0.706771
    2000-01-04 -1.039575  0.271860 -0.424972
    2000-01-05  0.567020  0.276232 -1.087401
    2000-01-06 -0.673690  0.113648 -1.478427
    2000-01-07  0.524988  0.404705  0.577046
    2000-01-08 -1.715002 -1.039268 -0.370647
    

    pandas对象(Index、Series和DataFrame)可以看做数组的容器,它们储存数据并且参与实际的计算。对于许多类型来说,它们底层的数组都是numpy.ndarray。然而,为了支持自定义数组,pandas和其他第三方库扩展了NumPy的类型系统。(请看dtypes
    如果要取出Index或Series中的数据,可以使用.array特性。

    In [11]: s.array
    Out[11]: 
    <PandasArray>
    [ 0.46911229990718628, -0.28286334432866328,  -1.5090585031735124,
      -1.1356323710171934,   1.2121120250208506]
    Length: 5, dtype: float64
    
    In [12]: s.index.array
    Out[12]: 
    <PandasArray>
    ['a', 'b', 'c', 'd', 'e']
    Length: 5, dtype: object
    

    取出的数组将是一个扩展数组(ExtensionArray)类型。那么扩展数组是什么呢?为什么pandas要使用扩展数组?这两个问题超过了我们这章的“简介”的范围,详情请看dtypes
    如果你需要将Series转换为NumPy数组,请用to_numpy()或者numpy.asarray()。

    In [13]: s.to_numpy()
    Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])
    
    In [14]: np.asarray(s)
    Out[14]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])
    

    当Series或Index由扩展数组(ExtensionArray)支持时,to_numpy()操作困难会涉及复制数据和支配值(coercing values)。详情请看dtypes
    to_numpy()操作会对生成的numpy.ndarray有一些控制。拿时区举个例子。NumPy并没有一个数据类型来表示带有时区的时间,困难有两种补偿办法:
    1.创建一个带有时间戳(Timestamp)的numpy.ndarray对象,每个对象都附带正确的时区(tz)
    2.一个datetime64[ns]类型的numpy.ndarray,里面的数据值已经被转换到了UTC,同时时区信息被抹去了

    UTC(协调世界时),详情请看UTC

    可以用dtype=object来保留时区。(即第一种补方法)

    In [15]: ser = pd.Series(pd.date_range('2000', periods=2, tz="CET"))
    
    In [16]: ser.to_numpy(dtype=object)
    Out[16]: 
    array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'),
           Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')], dtype=object)
    

    或者抛弃时区,改用dtype='datetime64[ns]'

    In [17]: ser.to_numpy(dtype="datetime64[ns]")
    Out[17]: array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'], dtype='datetime64[ns]')
    

    从DataFrame中取出原始数据可能有点复杂。如果DataFrame中所有的列都只有一种数据类型,那么DataFrame.to_numpy()将会返回底层数据。

    In [18]: df.to_numpy()
    Out[18]: 
    array([[-0.1732,  0.1192, -1.0442],
           [-0.8618, -2.1046, -0.4949],
           [ 1.0718,  0.7216, -0.7068],
           [-1.0396,  0.2719, -0.425 ],
           [ 0.567 ,  0.2762, -1.0874],
           [-0.6737,  0.1136, -1.4784],
           [ 0.525 ,  0.4047,  0.577 ],
           [-1.715 , -1.0393, -0.3706]])
    

    如果DataFrame或Panel包含同种类型的数据,n维数组(ndarray)能马上转换,这种变化将体现在数据结构中。对于不同类型的数据(比如DataFrame中列的数据类型并不都是一样的),事情就不一样了。值本身的属性将不会保留,这点和轴标签不太一样。
    注意: 对于不同类型的数据,生成的n维数组的数据类型将会改变来满足原数据的所有数据。比如,如果数据中有字符串,那么生成的数据类型将会是对象类型(object dtype)。如果只有浮点数和整数,生成的数组将会是浮点数类型(float dtype)。
    过去pandas推荐使用Series.valuesDataFrame.values来从Series或DataFrame中提取数据。老的代码库或者网上可能还能找到相关的链接。现在,我们推荐使用.array.to_numpy,避免使用.values。.values有以下缺点:
    1.如果你的Series中有扩展类型(extension type),很难辨别.values返回的是NumPy数组还是扩展数组。Series.array返回的总是ExtensionArray,并且不会复制数据。Series.to_numpy()返回的总是NumPy数组,可以进行开销很大的复制/支配值操作。
    2.如果你的DataFrame中含有多种数据类型,DataFrame.values操作可能会进行复制数据和支配值的操作,这会带来比较大的开销。
    DataFrame.to_numpy()作为一种方法,返回值更明确,返回的NumPy数组可能不是DataFrame同一数据上的视图。

    3.3.3 加速操作

    pandas支持使用numexpr库和瓶颈库加速某些类型的二进制数值和布尔运算。
    这些库在处理大数据集时非常有用,能提供大幅度的加速。numexpr使用智能分区、缓存和多核。bottleneck是一套特定的cython程序,在处理带有空值的数组时非常快。
    举个100列X10000行的DataFrame的例子:

    操作 0.11.0版本(ms) 先前的版本(ms) 比率
    df1>df2 13.32 125.35 0.1063
    df1*df2 21.71 36.63 0.5928
    df1+df2 22.04 36.50 0.6039

    这里0.11.0版本的含义待考证

    强烈建议安装这两个库。更多安装的信息请看推荐的依赖库
    这两个库都是默认安装并开启的,通过下面的设置可以控制:
    此操作是pandas0.20.0版本的新特性

    pd.set_option('compute.use_bottleneck', False)
    pd.set_option('compute.use_numexpr', False)
    

    3.3.4 灵活的二进制操作

    对于pandas数据结构之间的二进制操作,有两个关键点:

    • 在高维对象(如DataFrame)和低维对象(如Series)之间进行行为推广(broadcasting behavior)
    • 计算中的缺失数据
      虽然这两个问题能被同时解决,但是我们将分别说明如何处理这些问题。

    行为推广(broadcasting behavior):指将某个方法运用到Series、DataFrame等数据结构的某几行或某几列的所有数据中,即操作沿着一条轴进行/推广。

    3.3.4.1 匹配/传播行为

    对于二进制操作,DataFrame有add()sub()mul()div()等方法和相关函数如radd()rsub()等。对于行为推广来说,Series的输入是首要关注点。用这些函数时,通过axis关键字能够选择在行或列上推广。

    In [19]: df = pd.DataFrame({
       ....:     'one': pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
       ....:     'two': pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
       ....:     'three': pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
       ....: 
    
    In [20]: df
    Out[20]: 
            one       two     three
    a  1.400810 -1.643041       NaN
    b -0.356470  1.045911  0.395023
    c  0.797268  0.924515 -0.007090
    d       NaN  1.553693 -1.670830
    
    In [21]: row = df.iloc[1]
    
    In [22]: column = df['two']
    
    In [23]: df.sub(row, axis='columns')
    Out[23]: 
            one       two     three
    a  1.757280 -2.688953       NaN
    b  0.000000  0.000000  0.000000
    c  1.153738 -0.121396 -0.402113
    d       NaN  0.507782 -2.065853
    
    In [24]: df.sub(row, axis=1)
    Out[24]: 
            one       two     three
    a  1.757280 -2.688953       NaN
    b  0.000000  0.000000  0.000000
    c  1.153738 -0.121396 -0.402113
    d       NaN  0.507782 -2.065853
    
    In [25]: df.sub(column, axis='index')
    Out[25]: 
            one  two     three
    a  3.043851  0.0       NaN
    b -1.402381  0.0 -0.650888
    c -0.127247  0.0 -0.931605
    d       NaN  0.0 -3.224524
    
    In [26]: df.sub(column, axis=0)
    Out[26]: 
            one  two     three
    a  3.043851  0.0       NaN
    b -1.402381  0.0 -0.650888
    c -0.127247  0.0 -0.931605
    d       NaN  0.0 -3.224524
    

    sub()是做减法,axis='columns'或1是沿着列方向减去b行的每个值,axis='index'或0是沿着列方向减去df['two']的每个值。

    此外,还可以将多索引DataFrame的索引层与Series对齐。

    In [27]: dfmi = df.copy()
    
    In [28]: dfmi.index = pd.MultiIndex.from_tuples([(1, 'a'), (1, 'b'),
       ....:                                         (1, 'c'), (2, 'a')],
       ....:                                        names=['first', 'second'])
       ....: 
    
    In [29]: dfmi.sub(column, axis=0, level='second')
    Out[29]: 
                       one       two     three
    first second                              
    1     a       3.043851  0.000000       NaN
          b      -1.402381  0.000000 -0.650888
          c      -0.127247  0.000000 -0.931605
    2     a            NaN  3.196734 -0.027789
    

    注意这里只有first=1、second=['a','b','c']与Series(即上面的要减的coLumn)对齐了,所以在结果中的first=2、second='a'没有变化。

    在Panel中,描述行为推广有一点困难,我们提供了算术方法(可能有点令人困惑)来供你指定推广轴(broadcast axis)。举个例子,假设我们希望沿着一条特定的轴对数据进行降级(demean)。这可以通过在轴上取平均值并在同一轴上广播来实现。

    In [30]: major_mean = wp.mean(axis='major')
    
    In [31]: major_mean
    Out[31]: 
          Item1     Item2
    A -0.378069  0.675929
    B -0.241429 -0.018080
    C -0.597702  0.129006
    D  0.204005  0.245570
    
    In [32]: wp.sub(major_mean, axis='major')
    Out[32]: 
    <class 'pandas.core.panel.Panel'>
    Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
    Items axis: Item1 to Item2
    Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
    Minor_axis axis: A to D
    

    对于axis="items"和axis="minor"也一样。
    注意: 我(指pandas作者)本可以让DataFrame和Panel的行为推广的 axis 参数设置成一样的。虽然这需要一段转换期,以便于用户能够更改他们的代码。

    这里举个的例子的代码不全,应该是从文档的某个地方摘出来的,后续译者再补充进去。

    Series和Index也支持python内置的divmod()函数。这个函数同时进行整除除和取余操作,返回一个包含商和余数的元组(a // b, a % b),元组的类型和左边的a参数一致。例如:

    In [33]: s = pd.Series(np.arange(10))
    
    In [34]: s
    Out[34]: 
    0    0
    1    1
    2    2
    3    3
    4    4
    5    5
    6    6
    7    7
    8    8
    9    9
    dtype: int64
    
    In [35]: div, rem = divmod(s, 3)
    
    In [36]: div
    Out[36]: 
    0    0
    1    0
    2    0
    3    1
    4    1
    5    1
    6    2
    7    2
    8    2
    9    3
    dtype: int64
    
    In [37]: rem
    Out[37]: 
    0    0
    1    1
    2    2
    3    0
    4    1
    5    2
    6    0
    7    1
    8    2
    9    0
    dtype: int64
    
    In [38]: idx = pd.Index(np.arange(10))
    
    In [39]: idx
    Out[39]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
    
    In [40]: div, rem = divmod(idx, 3)
    
    In [41]: div
    Out[41]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')
    
    In [42]: rem
    Out[42]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
    

    也可以进行元素级别的divmod()

    In [43]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])
    
    In [44]: div
    Out[44]: 
    0    0
    1    0
    2    0
    3    1
    4    1
    5    1
    6    1
    7    1
    8    1
    9    1
    dtype: int64
    
    In [45]: rem
    Out[45]: 
    0    0
    1    1
    2    2
    3    0
    4    0
    5    1
    6    1
    7    2
    8    2
    9    3
    dtype: int64
    

    这里不是对Series的每个元素分别除以同样的除数,而是除以相对应的除数。

    3.3.4.2 缺失数据的填充

    在Series和DataFrame中,算术函数有一个可选的输入参数fill_value,即当数据结构中至多只有少数缺失值时可以用一个值来代替。例如,当把两个DataFrame相加时,你可能想把NaN当作0来处理,除非两个DataFrame的这个值都是NaN。但是相加的实际情况是只要两个DataFrame中有一个为NaN,相加的结果就为NaN。(不过如果你愿意,你之后也可以用fillna()函数来替换掉那些NaN值。)

    In [46]: df
    Out[46]: 
            one       two     three
    a  1.400810 -1.643041       NaN
    b -0.356470  1.045911  0.395023
    c  0.797268  0.924515 -0.007090
    d       NaN  1.553693 -1.670830
    
    In [47]: df2
    Out[47]: 
            one       two     three
    a  1.400810 -1.643041  1.000000
    b -0.356470  1.045911  0.395023
    c  0.797268  0.924515 -0.007090
    d       NaN  1.553693 -1.670830
    
    In [48]: df + df2
    Out[48]: 
            one       two     three
    a  2.801620 -3.286083       NaN
    b -0.712940  2.091822  0.790046
    c  1.594536  1.849030 -0.014180
    d       NaN  3.107386 -3.341661
    
    In [49]: df.add(df2, fill_value=0)
    Out[49]: 
            one       two     three
    a  2.801620 -3.286083  1.000000
    b -0.712940  2.091822  0.790046
    c  1.594536  1.849030 -0.014180
    d       NaN  3.107386 -3.341661
    
    3.3.4.3 灵活的比较

    Series和DataFrame有二进制的比较方法eq、ne、lt、gt、le和ge,操作和上面描述的二进制算术操作类似。

    In [50]: df.gt(df2)
    Out[50]: 
         one    two  three
    a  False  False  False
    b  False  False  False
    c  False  False  False
    d  False  False  False
    
    In [51]: df2.ne(df)
    Out[51]: 
         one    two  three
    a  False  False   True
    b  False  False  False
    c  False  False  False
    d   True  False  False
    

    这些操作会创建一个和左边输入的对象同样结构的布尔类型的pandas对象。新产生的布尔值对象能用在索引操作中,详情请看布尔索引部分

    3.3.4.4 布尔值约简

    你可能会使用emptyany()all()bool()等约简方式来汇总得到的布尔值。

    In [52]: (df > 0).all()
    Out[52]: 
    one      False
    two      False
    three    False
    dtype: bool
    
    In [53]: (df > 0).any()
    Out[53]: 
    one      True
    two      True
    three    True
    dtype: bool
    

    你能约简到最终的结果:

    In [54]: (df > 0).any().any()
    Out[54]: True
    

    只有组内有一个值是true,any()就会返回true

    通过empty特性,能够测试pandas对象是否为空。

    In [55]: df.empty
    Out[55]: False
    
    In [56]: pd.DataFrame(columns=list('ABC')).empty
    Out[56]: True
    

    判断单个元素的pandas对象的真假,可以用bool()方法:

    In [57]: pd.Series([True]).bool()
    Out[57]: True
    
    In [58]: pd.Series([False]).bool()
    Out[58]: False
    
    In [59]: pd.DataFrame([[True]]).bool()
    Out[59]: True
    
    In [60]: pd.DataFrame([[False]]).bool()
    Out[60]: False
    

    <u></u><font color=red>警告:</font>你可能会尝试以下操作:

    >>> if df:
    ...     pass
    

    或者

    >>> df and df2
    

    这两种操作都会导致错误,因为你在试图比较多个值:

    ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
    #值错误:数组的真值是含混的。请使用a.empty,a.any()或者a.all()。
    

    详情请看gotchas

    3.3.4.5 比较对象是否相等

    你经常会发现计算出同样结果的方式不止一种。举个简单的例子,比如df+df和df*2。要测试这两个计算是否产生同样的结果,考虑到上面讲的工具,你可能会想用(df+df==df*2).all()。但是事实上,这个表达是错的。

    In [61]: df + df == df * 2
    Out[61]: 
         one   two  three
    a   True  True  False
    b   True  True   True
    c   True  True   True
    d  False  True   True
    
    In [62]: (df + df == df * 2).all()
    Out[62]: 
    one      False
    two       True
    three    False
    dtype: bool
    

    注意布尔值的DataFrame df+df==df*2包含一些False值。这是因为NaNs并不被视为相等:

    In [63]: np.nan == np.nan
    Out[63]: False
    

    因此,n维结构(比如Series、DataFrame和Panel)有equals()方法来比较是否相等,equals()方法会把对应位置的NaN值视为相等。

    In [64]: (df + df).equals(df * 2)
    Out[64]: True
    

    注意Series和DataFrame的索引要是同样的顺序,这样才会被识别为相等。

    In [65]: df1 = pd.DataFrame({'col': ['foo', 0, np.nan]})
    
    In [66]: df2 = pd.DataFrame({'col': [np.nan, 0, 'foo']}, index=[2, 1, 0])
    
    In [67]: df1.equals(df2)
    Out[67]: False
    
    In [68]: df1.equals(df2.sort_index())
    Out[68]: True
    
    3.3.4.6 比较类似数组的对象

    当把pandas数据结构同一个标量值比较时,很容易就能进行元素级别的比较:

    In [69]: pd.Series(['foo', 'bar', 'baz']) == 'foo'
    Out[69]: 
    0     True
    1    False
    2    False
    dtype: bool
    
    In [70]: pd.Index(['foo', 'bar', 'baz']) == 'foo'
    Out[70]: array([ True, False, False], dtype=bool)
    

    pandas也支持不同的数组类(比如Series、Index、np.array)之间的元素级别的比较:

    In [71]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux'])
    Out[71]: 
    0     True
    1     True
    2    False
    dtype: bool
    
    In [72]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
    Out[72]: 
    0     True
    1     True
    2    False
    dtype: bool
    

    但是比较不同长度的Series或Index对象时会报错:

    In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
    ValueError: Series lengths must match to compare
    
    In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
    ValueError: Series lengths must match to compare
    

    注意,这和NumPy不同,在后者中比较是可以传递的:

    In [73]: np.array([1, 2, 3]) == np.array([2])
    Out[73]: array([False,  True, False], dtype=bool)
    

    如果比较不能传递,会返回False:

    In [74]: np.array([1, 2, 3]) == np.array([1, 2])
    Out[74]: False
    
    3.3.4.7 组合有重叠部分的数据集

    在组合两个相似的并且其中一个优先级更高的数据集时,有时会产生一个问题,举个例子,这里有两个Series同时呈现一个特定的经济指标,但是其中的质量更高。但是,低质量的Series可能能回溯更多的历史数据和保留更多的原始数据。正因为如此,在组合两个DataFrame时,我们更愿意把更好的数据集中的缺失值有条件地用其他DataFrame中的相似标签的值来代替。执行这个操作的函数是combine_first(),具体请看下面的解释:

    In [75]: df1 = pd.DataFrame({'A': [1., np.nan, 3., 5., np.nan],
       ....:                     'B': [np.nan, 2., 3., np.nan, 6.]})
       ....: 
    
    In [76]: df2 = pd.DataFrame({'A': [5., 2., 4., np.nan, 3., 7.],
       ....:                     'B': [np.nan, np.nan, 3., 4., 6., 8.]})
       ....: 
    
    In [77]: df1
    Out[77]: 
         A    B
    0  1.0  NaN
    1  NaN  2.0
    2  3.0  3.0
    3  5.0  NaN
    4  NaN  6.0
    
    In [78]: df2
    Out[78]: 
         A    B
    0  5.0  NaN
    1  2.0  NaN
    2  4.0  3.0
    3  NaN  4.0
    4  3.0  6.0
    5  7.0  8.0
    
    In [79]: df1.combine_first(df2)
    Out[79]: 
         A    B
    0  1.0  NaN
    1  2.0  2.0
    2  3.0  3.0
    3  5.0  4.0
    4  3.0  6.0
    5  7.0  8.0
    

    A中的NaN值用B中的值代替了。A中缺少index=5的值,也用B中的值补全了。

    3.3.4.8 一般的DataFrame组合

    上面的combine_first()方法调用更通用的dataframe.combine(),此方法采用另一个DataFrame和合并器函数,对齐输入的DataFrame,然后传递序列的合并器函数对(即名称相同的列)。
    例如,现在要复现上面的combine_first():

    In [80]: def combiner(x, y):
       ....:     np.where(pd.isna(x), y, x)
       ....: df1.combine(df2, combiner)
       ....: 
    Out[80]: 
         A    B
    0  NaN  NaN
    1  NaN  NaN
    2  NaN  NaN
    3  NaN  NaN
    4  NaN  NaN
    5  NaN  NaN
    

    注意这里的文档有误,combiner函数里应该加个return关键字,正是因为缺少返回值,所以最后输出的结果全为空值。下面请看改正后运行的结果:

    In[22]:def combiner(x, y):
        return np.where(pd.isna(x), y, x)
    df1.combine(df2, combiner)
    
    Out[22]:
         A    B   
    0   1.0 NaN
    1   2.0 2.0
    2   3.0 3.0
    3   5.0 4.0
    4   3.0 6.0
    5   7.0 8.0
    

    3.3.5 统计描述

    pandas对于Series、DataFrame和Panel的统计描述计算和相关操作有很多方法。其中大多数都是聚合函数(会产生一个比原结构维度低的结果),比如sum()mean()quantile(),但是也有一些别的函数,会返回和原结构同样维度大小的结果,比如cumsum()cumprod()
    通常来讲,ndarray.{sum,std,...}这些方法需要一个axis(轴)参数,可以通过整数或者名字指定:

    • Series:不需要axis参数
    • DataFrame:"index"(axis=0,默认值),"columns"(axis=1)
    • Panel:"items"(axis=0),"major"(axis=1,默认值),"minor"(axis=2)
      For example:
    In [81]: df
    Out[81]: 
            one       two     three
    a  1.400810 -1.643041       NaN
    b -0.356470  1.045911  0.395023
    c  0.797268  0.924515 -0.007090
    d       NaN  1.553693 -1.670830
    
    In [82]: df.mean(0)
    Out[82]: 
    one      0.613869
    two      0.470270
    three   -0.427633
    dtype: float64
    
    In [83]: df.mean(1)
    Out[83]: 
    a   -0.121116
    b    0.361488
    c    0.571564
    d   -0.058569
    dtype: float64
    

    所有方法都有一个可选参数skipna,可以决定是否去除缺失值(skipna默认是True):

    In [84]: df.sum(0, skipna=False)
    Out[84]: 
    one           NaN
    two      1.881078
    three         NaN
    dtype: float64
    
    In [85]: df.sum(axis=1, skipna=True)
    Out[85]: 
    a   -0.242232
    b    1.084464
    c    1.714693
    d   -0.117137
    dtype: float64
    

    不去除缺失值的话,只要有NaN,就会被计算为NaN。

    结合推广/算术行为,可以非常简洁地描述各种统计过程,如标准化(standardization)(呈现数据零均值化(zero-mean)和标准差):

    In [86]: ts_stand = (df - df.mean()) / df.std()
    
    In [87]: ts_stand.std()
    Out[87]: 
    one      1.0
    two      1.0
    three    1.0
    dtype: float64
    
    In [88]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)
    
    In [89]: xs_stand.std(1)
    Out[89]: 
    a    1.0
    b    1.0
    c    1.0
    d    1.0
    dtype: float64
    

    注意cumsum()和cumprod()方法保存了NaN值的位置。这与expanding()rolling()有点不同。详情请看注释

    In [90]: df.cumsum()
    Out[90]: 
            one       two     three
    a  1.400810 -1.643041       NaN
    b  1.044340 -0.597130  0.395023
    c  1.841608  0.327385  0.387933
    d       NaN  1.881078 -1.282898
    

    下面是常用函数的快速参考摘要表。如果目标对象有多重索引,每个函数还可以传入可选的level参数。

    函数 描述
    count 非空的观察值的数量
    sum 求和
    mean 平均值
    mad 平均绝对偏差
    median 算术中值
    min 最小值
    max 最大值
    mode 模式
    abs 绝对值
    prod 乘积
    std Bessel校正样品标准差
    var 无偏方差
    sem 平均值的标准误差
    skew 样品偏度(第三力矩)
    kurt 样品峰度(第四力矩)
    quantile 样品分位数(值为百分数)
    cumsum 累积和
    cumprod 累积积
    cummax 累积最大值
    cummin 累积最小值

    请注意一些NumPy方法比如mean、std和sum,可能会默认排除Series中的空值(NA)。

    In [91]: np.mean(df['one'])
    Out[91]: 0.6138692844180106
    
    In [92]: np.mean(df['one'].to_numpy())
    Out[92]: nan
    

    series.nunique()将返回序列中所有唯一的非NA值的数目:

    In [93]: series = pd.Series(np.random.randn(500))
    
    In [94]: series[20:500] = np.nan
    
    In [95]: series[10:20] = 5
    
    In [96]: series.nunique()
    Out[96]: 11
    
    3.3.5.1 数据摘要:describe函数

    describe()这个函数很方便,它能能计算Series或DataFrame的各列的各种统计描述值(当然会排除空值):

    In [97]: series = pd.Series(np.random.randn(1000))
    
    In [98]: series[::2] = np.nan
    
    In [99]: series.describe()
    Out[99]: 
    count    500.000000
    mean      -0.020695
    std        1.011840
    min       -2.683763
    25%       -0.709297
    50%       -0.070211
    75%        0.712856
    max        3.160915
    dtype: float64
    
    In [100]: frame = pd.DataFrame(np.random.randn(1000, 5),
       .....:                      columns=['a', 'b', 'c', 'd', 'e'])
       .....: 
    
    In [101]: frame.iloc[::2] = np.nan
    
    In [102]: frame.describe()
    Out[102]: 
                    a           b           c           d           e
    count  500.000000  500.000000  500.000000  500.000000  500.000000
    mean     0.026515    0.022952   -0.047307   -0.052551    0.011210
    std      1.016752    0.980046    1.020837    1.008271    1.006726
    min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
    25%     -0.647623   -0.593587   -0.709906   -0.691338   -0.689176
    50%      0.047578   -0.026675   -0.029655   -0.032769   -0.015775
    75%      0.723946    0.771931    0.603753    0.667044    0.652221
    max      2.740139    2.752332    3.004229    2.728702    3.240991
    

    而且你能自定义要在结果中展现的具体百分数值:

    In [103]: series.describe(percentiles=[.05, .25, .75, .95])
    Out[103]: 
    count    500.000000
    mean      -0.020695
    std        1.011840
    min       -2.683763
    5%        -1.641337
    25%       -0.709297
    50%       -0.070211
    75%        0.712856
    95%        1.699176
    max        3.160915
    dtype: float64
    

    结果会默认包含中位数。
    对于非数值类的Series对象,describe()函数只会给出一个简单的统计,包括唯一值的数目和出现最频繁的值。

    In [104]: s = pd.Series(['a', 'a', 'b', 'b', 'a', 'a', np.nan, 'c', 'd', 'a'])
    
    In [105]: s.describe()
    Out[105]: 
    count     9
    unique    4
    top       a
    freq      5
    dtype: object
    

    另外对于混合类型的DataFrame对象,describe()将会只给出数值型列的摘要,如果没有数值型的列,那么只会给出category类的列的摘要:

    In [106]: frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})
    
    In [107]: frame.describe()
    Out[107]: 
                  b
    count  4.000000
    mean   1.500000
    std    1.290994
    min    0.000000
    25%    0.750000
    50%    1.500000
    75%    2.250000
    max    3.000000
    

    这个函数也能自定义,只需要传入include/exclude参数就行。特殊值也可以使用:

    In [108]: frame.describe(include=['object'])
    Out[108]: 
              a
    count     4
    unique    2
    top     Yes
    freq      2
    
    In [109]: frame.describe(include=['number'])
    Out[109]: 
                  b
    count  4.000000
    mean   1.500000
    std    1.290994
    min    0.000000
    25%    0.750000
    50%    1.500000
    75%    2.250000
    max    3.000000
    
    In [110]: frame.describe(include='all')
    Out[110]: 
              a         b
    count     4  4.000000
    unique    2       NaN
    top     Yes       NaN
    freq      2       NaN
    mean    NaN  1.500000
    std     NaN  1.290994
    min     NaN  0.000000
    25%     NaN  0.750000
    50%     NaN  1.500000
    75%     NaN  2.250000
    max     NaN  3.000000
    

    这个功能依赖于选择数据类型。有关可行的输入的详细信息,请参阅此处。

    3.3.5.2 最小/大值索引

    idxmin()idxmax()函数能计算出Series和DataFrame中最小/大值的索引标签。

    In [111]: s1 = pd.Series(np.random.randn(5))
    
    In [112]: s1
    Out[112]: 
    0   -0.068822
    1   -1.129788
    2   -0.269798
    3   -0.375580
    4    0.513381
    dtype: float64
    
    In [113]: s1.idxmin(), s1.idxmax()
    Out[113]: (1, 4)
    
    In [114]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=['A', 'B', 'C'])
    
    In [115]: df1
    Out[115]: 
              A         B         C
    0  0.333329 -0.910090 -1.321220
    1  2.111424  1.701169  0.858336
    2 -0.608055 -2.082155 -0.069618
    3  1.412817 -0.562658  0.770042
    4  0.373294 -0.965381 -1.607840
    
    In [116]: df1.idxmin(axis=0)
    Out[116]: 
    A    2
    B    2
    C    4
    dtype: int64
    
    In [117]: df1.idxmax(axis=1)
    Out[117]: 
    0    A
    1    A
    2    C
    3    A
    4    A
    dtype: object
    

    如果有多个行或列符合最小/大值,idxmin()和idxmax()会返回第一个匹配的索引。

    In [118]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'], index=list('edcba'))
    
    In [119]: df3
    Out[119]: 
         A
    e  2.0
    d  1.0
    c  1.0
    b  3.0
    a  NaN
    
    In [120]: df3['A'].idxmin()
    Out[120]: 'd'
    

    注意: idxmin和idxmax在NumPy中被叫做argmin和argmax。

    3.3.5.3 计数(histogramming)/模式

    Series的value_counts()方法和顶级函数会计算出一维数组的柱状图。对于常规的数组也可以用这个方法:

    In [121]: data = np.random.randint(0, 7, size=50)
    
    In [122]: data
    Out[122]: 
    array([6, 4, 1, 3, 4, 4, 4, 6, 5, 2, 6, 1, 0, 4, 3, 2, 5, 3, 4, 0, 5, 3, 0,
           1, 5, 0, 1, 5, 3, 4, 1, 2, 3, 2, 4, 6, 1, 4, 3, 5, 2, 1, 2, 4, 1, 6,
           3, 6, 3, 3])
    
    In [123]: s = pd.Series(data)
    
    In [124]: s.value_counts()
    Out[124]: 
    4    10
    3    10
    1     8
    6     6
    5     6
    2     6
    0     4
    dtype: int64
    
    In [125]: pd.value_counts(data)
    Out[125]: 
    4    10
    3    10
    1     8
    6     6
    5     6
    2     6
    0     4
    dtype: int64
    

    类似的,在Series和DataFrame中,你也能获取最常出现的值或模式:

    In [126]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])
    
    In [127]: s5.mode()
    Out[127]: 
    0    3
    1    7
    dtype: int64
    
    In [128]: df5 = pd.DataFrame({"A": np.random.randint(0, 7, size=50),
       .....:                     "B": np.random.randint(-10, 15, size=50)})
       .....: 
    
    In [129]: df5.mode()
    Out[129]: 
       A  B
    0  0 -9
    
    3.3.5.4 离散化和分位数

    连续值能够通过cut()(容器基于值)和qcut()(容器基于样品分位数)函数进行离散化。(结果会得到很多容器)

    In [130]: arr = np.random.randn(20)
    
    In [131]: factor = pd.cut(arr, 4)
    
    In [132]: factor
    Out[132]: 
    [(1.27, 2.31], (0.231, 1.27], (-0.809, 0.231], (-1.853, -0.809], (1.27, 2.31], ..., (0.231, 1.27], (-0.809, 0.231], (-1.853, -0.809], (1.27, 2.31], (0.231, 1.27]]
    Length: 20
    Categories (4, interval[float64]): [(-1.853, -0.809] < (-0.809, 0.231] < (0.231, 1.27] < (1.27, 2.31]]
    
    In [133]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])
    
    In [134]: factor
    Out[134]: 
    [(1, 5], (0, 1], (-1, 0], (-5, -1], (1, 5], ..., (1, 5], (-1, 0], (-5, -1], (1, 5], (0, 1]]
    Length: 20
    Categories (4, interval[int64]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]
    

    qcut()计算出样品分位数。例如,我们能够把一些常规分布的数据切分成同样大小的分段:

    In [135]: arr = np.random.randn(30)
    
    In [136]: factor = pd.qcut(arr, [0, .25, .5, .75, 1])
    
    In [137]: factor
    Out[137]: 
    [(-2.219, -0.669], (-0.669, 0.00453], (0.367, 2.369], (0.00453, 0.367], (0.367, 2.369], ..., (0.00453, 0.367], (0.367, 2.369], (0.00453, 0.367], (-0.669, 0.00453], (0.367, 2.369]]
    Length: 30
    Categories (4, interval[float64]): [(-2.219, -0.669] < (-0.669, 0.00453] < (0.00453, 0.367] <
                                        (0.367, 2.369]]
    
    In [138]: pd.value_counts(factor)
    Out[138]: 
    (0.367, 2.369]       8
    (-2.219, -0.669]     8
    (0.00453, 0.367]     7
    (-0.669, 0.00453]    7
    dtype: int64
    

    我们也能传递无限制来定义容器:

    In [139]: arr = np.random.randn(20)
    
    In [140]: factor = pd.cut(arr, [-np.inf, 0, np.inf])
    
    In [141]: factor
    Out[141]: 
    [(0.0, inf], (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (0.0, inf], (-inf, 0.0], (-inf, 0.0]]
    Length: 20
    Categories (2, interval[float64]): [(-inf, 0.0] < (0.0, inf]]
    

    相关文章

      网友评论

        本文标题:pandas文档0.24.1第三章 马上开始(二)

        本文链接:https://www.haomeiwen.com/subject/gfuvmqtx.html