Pandas的数据结构

Kopei article

前言

基本上, PandasSeries沿袭了Numpy的设计, 是一维数组和数组的索引, 和Numpy不同的一点是这个一维数组可以是异构的,比如数字int和字符串混在一起, 这个时候数组的类型是object. 而DataFrame是为了处理表格和异构多维的数据.

Series

可以用python的list初始化一个Series, 也可以用python的Dict初始化一个Series. 如果使用Dict那么序列的索引就是DictKey.

1
2
3
4
5
>>> d = {'d': 1, 'e':2, 'a':0}
>>> pd.Series(d)
a 0
d 1
e 2

想要得到自己想要的顺序可以使用参数index排序Series, 下图的例子可以看到索引b, 但是它没有值会用NaN表示(可以使用isnull, notnull检测是否为空), 没有索引的值会被省略.

1
2
3
4
5
>>> pd.Series(d, index=['e','b','d'])
e 2.0
b NaN
d 1.0
dtype: float64

Pandas筛选比较强大的一点是直接可以在[]输入条件, 这点可能和python本身的语法有一点不同.

1
2
3
4
5
6
7
8
9
>>> obj2= pd.Series(d)
>>> obj2
a 0
d 1
e 2
dtype: int64
>>> obj2 [ obj2 > 1]
e 2
dtype: int64

两个Series是可以直接通过索引直接做计算的, 类似数据库的join. 计算子必须都需要有相同的索引才会有计算结果, 否则会出现NaN.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
In [35]: obj3
Out[35]:
Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64

In [36]: obj4
Out[36]:
California NaN
Ohio 35000
Oregon 16000
Texas 71000
dtype: float64

In [37]: obj3 + obj4
Out[37]:
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN
dtype: float64

可以给Series起一个名字, 使用Series.name属性, 取个名字的好处是如果把Series放入Dataframe, 那么这个name就是Dataframe的列名.(DataFrame也会有index名称和column名称)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [1]: s = pd.Series(["A","B","C"], name="foo")

In [2]: s
Out[2]:
0 A
1 B
2 C
Name: foo, dtype: object

In [3]: pd.DataFrame(s)
Out[4]:
foo
0 A
1 B
2 C

DataFrame

PandasDataFrame代表了一组表格(一般是二维, 可以使用hierarchical indexing来表示更高的维度数组), 数据的底层存储如下图所示, 数据会按类型分成不同的块存储实际的数据. 可以看到数据被没有指向具体的列, 而是通过BlockManager这个类是来管理实际数据到列名的映射.
https://s3.ap-southeast-1.amazonaws.com/kopei-public/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-09-16%20%E4%B8%8B%E5%8D%883.29.53.png
有很多种方式构建DataFrame, 常见的一种是通过Dict; 如果想在创建的时候指定索引, 可以使用嵌入式dict.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [44]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
In [45]: frame = pd.DataFrame(data)
In [46]: frame
Out[46]:
pop state year
0 1.5 Ohio 2000
1 1.7 Ohio 2001
2 3.6 Ohio 2002
3 2.4 Nevada 2001
4 2.9 Nevada 2002
5 3.2 Nevada 2003

>>> pop = {'Nevada': {2001: 2.4, 2002: 2.9},'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
>>> frame3 = pd.DataFrame(pop)
>>> frame3
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6

下图是所有的输入数据类型可以构建DataFrame:
https://s3.ap-southeast-1.amazonaws.com/kopei-public/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-09-17%20%E4%B8%8B%E5%8D%882.19.37.png

DataFrame可以指定column参数选择只要某几列, 也可以在column任意添加列名, 如何column这个字段赋予了不存在的列, 那么新加的这个列会显示NaN. 两者的区别在于使用属性访问时, 这个python的attribute必须符合python的规则.

1
2
3
4
5
6
7
8
>>> pd.DataFrame(data, columns=['pop','jik'])
pop jik
0 1.5 NaN
1 1.7 NaN
2 3.6 NaN
3 2.4 NaN
4 2.9 NaN
5 3.2 NaN

想要取得DataFrame的某个列, 可以采用.columnName或切片的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [51]: frame2['state']
Out[51]:
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
six Nevada
Name: state, dtype: object

In [52]: frame2.year
Out[52]:
one 2000
two 2001
three 2002
four 2001
five 2002
six 2003
Name: year, dtype: int64

通过DataFrame.loc[]可以选取某几行, 这个loc用的是[] ,而不是(), 和python的语法又有些区别. 一般使用loc[]定位字符串(label)索引, iloc[]定位数字(number)索引.

1
2
3
4
5
>>> frame.iloc[3]
pop 2.4
state Nevada
year 2001
Name: 3, dtype: object

可以使用SeriesDataFrame赋值, Series将成为DataFrame的一个列, 列值必须和DataFrame的长度一致, 缺失的值将会NaN.

1
2
3
4
5
6
7
8
9
10
>>> val = pd.Series([-1.2, -1.5, -1.9], index=['two','four','five'])
>>> frame2['debt'] = val
>>> frame2
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 -1.2
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 -1.5
five 2002 Nevada 2.9 -1.9
six 2003 Nevada 3.2 NaN

DateFrame列赋值还有一个快捷判断的语法, 可以一行代码先判断再赋值

1
2
3
4
5
6
7
8
9
>>> frame2['new_col'] = frame2.state == 'Ohio'
>>> frame2
year state pop debt new_col
one 2000 Ohio 1.5 NaN True
two 2001 Ohio 1.7 -1.2 True
three 2002 Ohio 3.6 NaN True
four 2001 Nevada 2.4 -1.5 False
five 2002 Nevada 2.9 -1.9 False
six 2003 Nevada 3.2 NaN False

列的删除: 使用del df[clo]
DataFramevalues属性会返回一个二维ndarray的数组.

1
2
3
4
>>> frame3.values
array([[nan, 1.5],
[2.4, 1.7],
[2.9, 3.6]])

Index对象

Index对象负责存储轴标签axis label和其它元信息如索引名称, 任何数组或其它序列的标签在转化成SeriesDataFrame的时候会被转成Index.

1
2
3
4
5
6
7
8
9
10
11
>>> lables = pd.Index(np.arange(3))
>>> lables
Int64Index([0, 1, 2], dtype='int64')
>>> obj4 = pd.Series([1.4,3,1], index=lables)
>>> obj4
0 1.4
1 3.0
2 1.0
dtype: float64
>>> obj4.index is lables
True

pandas的索引对象是不可变的, 既可以把index当成数组, 可以当成集合, 注意这个集合不同于python的set, 集合是可以重复的.

1
2
3
>>> dup_index = pd.Index(['foo','foo','bar'])
>>> dup_index
Index([u'foo', u'foo', u'bar'], dtype='object')

Index对象的一些常用方法:
https://s3.ap-southeast-1.amazonaws.com/kopei-public/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-09-17%20%E4%B8%8B%E5%8D%882.40.46.png

  • Post title:Pandas的数据结构
  • Post author:Kopei
  • Create time:2018-09-16 00:00:00
  • Post link:https://kopei.github.io/2018/09/15/python-2018-09-16-pandas的数据结构/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
 Comments
On this page
Pandas的数据结构