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为单位来存取效率会更高。
网友评论