数据分析之三:pandas库之基本数据结构

作者: 无为无悔 | 来源:发表于2016-08-15 23:10 被阅读0次

    1. pandas的两种主要数据结构


    pandas是基于NumPy的数据分析工具库,其运算功能和效率更加强大。如果说NumPy的核心数据结构是ndarray, pandas的主要数据结构分别是Series和DataFrame。pandas库的导入:

    from pandas import Series, DataFrame
    import pandas as pd
    

    顾名思义, Series是一个序列,可以用作描述一个一维数组,与我们一般理解的数组不同的是,Series显式的包含了数组的下标索引列,并且这个索引列可以由我们来自定义。Series的内存模型跟ndarray类似,都是连续存储。

    而DataFrame可以看成是更高级的Series,可以看成是多个Series组成的数据形式,它是以一个frame来表示数据,可以描述二维乃至更高维度的数据,它也显式的包含了二维数据的行列索引,使得数据的展示更像日程生活的表格。

    依据书中介绍,DataFrame的存储结构是以一个或多个二维数据块存放的,故对矩阵的行列操作的复杂度都是相平衡的,而其他语言大部分是以一维的形式存放的(行优先或者列优先),这里先不研究具体的内存模型,考虑ndarray的连续存储模型,可以大胆猜测,DataFrame的存取效率和运算效率高于普通一维数组运算,或者说DataFrame在处理二维数据上有着天生的优势。

    2. Series简单操作


    • 创建
    In [68]: arr = Series([5,4,3,2,1])
    
    # 简单赋值,索引列和数据列,最后是值的类型
    In [69]: arr
    Out[69]: 
    0    5
    1    4
    2    3
    3    2
    4    1
    dtype: int64
    
    # 索引列
    In [70]: arr.index
    Out[70]: RangeIndex(start=0, stop=5, step=1)
    # 值
    In [71]: arr.values
    Out[71]: array([5, 4, 3, 2, 1])
    
    # 可以指定索引列,这里索引-数据必须是一一对应,否则报错
    # 还有一点,我们指定的索引只是相对索引,绝对索引仍然是0,...,N-1
    # 这点有点类似数据库里面的RowId
    In [72]: arr = Series([5,4,3,2,1], index=['a','b','c','d','e'])
    
    In [73]: arr
    Out[73]: 
    a    5
    b    4
    c    3
    d    2
    e    1
    dtype: int64
    
    # 可以使用我们熟知的np来定义
    In [95]: arr = Series(randint(3,size=5))
    
    In [96]: arr
    Out[96]: 
    0    0
    1    2
    2    1
    3    1
    4    2
    dtype: int64
    
    # 更可以使用json格式或者字典格式来创建
    In [97]: json_str = {'a':1, 'b':2, 'c':3, 'd':4}
    
    In [98]: arr = Series(json_str)
    
    In [99]: arr
    Out[99]: 
    a    1
    b    2
    c    3
    d    4
    dtype: int64
    
    # 我们要指定的索引列的个数也可以变化
    # 多出来的列如果没有值,默认填NaN,表示此索引对应的值缺失
    # 但是前面定义的时候不允许我们这样做
    In [100]: idx = ['a', 'b', 'c', 'd', 'e']
    
    In [101]: arr = Series(arr, index=idx)
    
    In [102]: arr
    Out[102]: 
    a    1.0
    b    2.0
    c    3.0
    d    4.0
    e    NaN
    dtype: float64
    
    • 存取
    # 绝对索引访问
    In [78]: arr[2]
    Out[78]: 3
    # 我们定义的相对索引访问
    In [79]: arr['c']
    Out[79]: 3
    
    # 依然支持切片存取
    # 使用绝对索引
    In [82]: arr[0:3]
    Out[82]: 
    a    5
    b    4
    c    3
    dtype: int64
    
    # 使用相对索引
    In [83]: arr['a':'c']
    Out[83]: 
    a    5
    b    4
    c    3
    dtype: int64
    
    # 取任意一组
    In [86]: arr[['a','b','c']]
    Out[86]: 
    a    5
    b    4
    c    3
    dtype: int64
    

    注:使用切片时,个性化的相对索引是左右都包含的,绝对索引是右不包含,这里可能是pandas库人性化的设置,以防我们使用不规则的数据作为索引时容易失误。

    索引对象的进阶以及切片的使用放到下文详细讨论。

    3. DataFrame简单操作


    • 创建
    # 字典方式创建
    In [108]: frame_data = {'A':[1,2,3], 'B':[4,5,6], 'C':[7,8,9]}
    
    In [109]: frame = DataFrame(frame_data)
    
    In [110]: frame
    Out[110]: 
       A  B  C
    0  1  4  7
    1  2  5  8
    2  3  6  9
    
    # 字典基础上,指定行索引值
    In [111]: frame = DataFrame(frame_data, index=['row1', 'row2', 'row3'])
    
    In [112]: frame
    Out[112]: 
          A  B  C
    row1  1  4  7
    row2  2  5  8
    row3  3  6  9
    
    # 用np的矩阵来创建
    In [113]: arr = np.array([[1,2,3], [4,5,6], [7,8,9]])
    
    In [114]: arr
    Out[114]: 
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    
    In [115]: frame = DataFrame(arr)
    
    In [116]: frame
    Out[116]: 
       0  1  2
    0  1  2  3
    1  4  5  6
    2  7  8  9
    
    # 指定行和列索引
    In [120]: frame = DataFrame(arr, index=['row1', 'row2', 'row3'], columns=['col1', 'col2', 'col3'])
    
    In [121]: frame
    Out[121]: 
          col1  col2  col3
    row1     1     2     3
    row2     4     5     6
    row3     7     8     9
    
    # 缺失列值的填充,行的情况类似
    In [124]: frame = DataFrame(frame, columns=['col1', 'col2', 'col3', 'col4'])
    
    In [125]: frame
    Out[125]: 
          col1  col2  col3  col4
    row1     1     2     3   NaN
    row2     4     5     6   NaN
    row3     7     8     9   NaN
    
    # 用Series来创建
    In [191]: arr
    Out[191]: 
    0    1
    1    2
    2    3
    dtype: int64
    
    
    In [192]: frame = DataFrame([arr]*3)
    
    In [193]: frame
    Out[193]: 
       0  1  2
    0  1  2  3
    1  1  2  3
    2  1  2  3
    
    
    In [215]: frame = DataFrame({0:arr, 1:arr, 2:arr})
    
    In [216]: frame
    Out[216]: 
       0  1  2
    0  1  1  1
    1  2  2  2
    2  3  3  3
    
    • 存取
    # 取某一列
    In [125]: frame
    Out[125]: 
          col1  col2  col3  col4
    row1     1     2     3   NaN
    row2     4     5     6   NaN
    row3     7     8     9   NaN
    
    In [126]: frame['col2']
    Out[126]: 
    row1    2
    row2    5
    row3    8
    Name: col2, dtype: int64
    
    # 使用切片索引获取行
    In [135]: frame[0:3]
    Out[135]: 
          col1  col2  col3  col4
    row1     1     2     3   NaN
    row2     4     5     6   NaN
    row3     7     8     9   NaN
    
    In [136]: frame[0:2]
    Out[136]: 
          col1  col2  col3  col4
    row1     1     2     3   NaN
    row2     4     5     6   NaN
    
    # 也可以使用ix获取
    In [138]: frame.ix[0]
    Out[138]: 
    col1    1.0
    col2    2.0
    col3    3.0
    col4    NaN
    Name: row1, dtype: float64
    
    In [139]: frame.ix['row1']
    Out[139]: 
    col1    1.0
    col2    2.0
    col3    3.0
    col4    NaN
    Name: row1, dtype: float64
    
    # 获取任意行
    In [140]: frame.ix[[0,1]]
    Out[140]: 
          col1  col2  col3  col4
    row1     1     2     3   NaN
    row2     4     5     6   NaN
    
    # 获取任意行、任意列
    In [142]: frame.ix[[0, 1]][['col1', 'col2']]
    Out[142]: 
          col1  col2
    row1     1     2
    row2     4     5
    
    • 行列存取效率的比较
    # 获取任意一列的时间
    In [162]: frame['col2']
    Out[162]: 
    row1    2
    row2    5
    row3    8
    Name: col2, dtype: int64
    
    In [163]: %timeit frame['col2']
    The slowest run took 19.52 times longer than the fastest. This could mean that an intermediate result is being cached.
    100000 loops, best of 3: 2.36 µs per loop
    
    # 获取任意一行的时间
    In [164]: frame.ix['row1']
    Out[164]: 
    col1    1.0
    col2    2.0
    col3    3.0
    col4    NaN
    Name: row1, dtype: float64
    
    In [165]: %timeit frame.ix['row1']
    10000 loops, best of 3: 133 µs per loop
    
    #切片方式获取一行或多行的时间
    In [167]: %timeit frame[0:1]
    The slowest run took 5.04 times longer than the fastest. This could mean that an intermediate result is being cached.
    10000 loops, best of 3: 92.8 µs per loop
    
    # 获取某个“数据块”的时间
    In [170]: frame.ix[[0,1]][['col1', 'col2']]
    Out[170]: 
          col1  col2
    row1     1     2
    row2     4     5
    
    In [171]: %timeit frame.ix[[0,1]][['col1', 'col2']]
    1000 loops, best of 3: 881 µs per loop
    

    由此,可以看出,DataFrame不同方式的读取速度:列 > 行 > 数据块,这里验证了DataFrame由Series组成,以Series为单位来存取效率会更高。

    相关文章

      网友评论

        本文标题:数据分析之三:pandas库之基本数据结构

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