Pandas中含有两种主要的数据结构:Series和Dataframe,本篇笔记是第2篇,记录的是Dataframe。
关于Series的介绍:https://blog.csdn.net/JT_WPC/article/details/104135662
笔记内容的结构:
-
Dataframe简介;
-
构建Dataframe的几种情况;
-
Dataframe的常用操作。
Dataframe简介
DataFrame是一个二维的含有行标记的数据结构,列之间的数据类型可以不同。同时,Dataframe可以看做是Series的容器或者是字典,其中的一个列可以看做是一个Series,是最常用的panda对象。像Series一样,DataFrame接受多种类型的输入:
- 一维ndarray、列表、字典或者series的字典;
- 二维ndarray数据;
- 结构化的ndarray;
- 另一个dataframe。
构造Dataframe时,除了传递data参数之外,还可以选择传递index(行标签)参数和columns(列标签)参数,分别表示行名和列名。
同样的,dataframe中index标签和数据也是默认自动对齐,因此,当一个series的字典附加上特定的index参数,将筛选出与传递的index匹配的数据。
和Series一样,使用不同的python版本会导致一些细微的不同,推荐3.6及以上版本:如果使用Python版本>= 3.6和panda >= 0.23,那么当数据是一个dict,并且没有指定列时,DataFrame列将按照dict的插入顺序排序;如果您使用的是Python < 3.6或panda < 0.23,并且没有指定列,那么DataFrame列将按照dict中key的字典顺序对列表排序。
构建dataframe的几种情况
情况一:从字典的字典或者是Series的字典中获取数据
由于具有多个Series,相应的,也会有多个index集合,那么最后生成的Dataframe中的index取各个Series的index的并集。默认将dict的key的有序列表作为columns,当使用从字典中取值生成dataframe时,columns参数的作用是筛选器,选出和columns参数匹配的那部分数据,为columns中未被匹配到的部分,则会是NaN填充。
# 构建一个Series字典
In : d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
In : df = pd.DataFrame(d)
In : df
Out:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In : pd.DataFrame(d, index=['d', 'b', 'a']) # 使用index作为筛选器
Out:
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
In : pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three']) # 使用columns作为筛选器
Out:
two three
d 4.0 NaN
b 2.0 NaN
a 1.0 NaN
Dataframe的行和列的标签可以通过index属性和columns属性获取:
In : df.index
Out: Index(['a', 'b', 'c', 'd'], dtype='object')
In : df.columns
Out: Index(['one', 'two'], dtype='object')
情况二:从ndarray字典或者list字典中获取数据
所有ndarray的长度必须相同。如果传递了一个index参数,那么它的长度必须与数组的长度相同。如果没有传递index参数,则会自动生成0~ (n-1)的数字index,其中n是数组长度。
# ndarray字典的例子
# 实例化一个ndarray对象,并指定其中数据类型,i4、f4、a10分别是numpy中设定的整型、浮点型、字符串型类型名
In : data = np.zeros((2, ), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])
#注意,这里对data进行赋值的时候,带上[:]表示向上面的结构中填充数据,而直接对data赋值表示重新实例化一个序列对象而不具有设定好的结构
In : data[:] = [(1, 2., 'Hello'), (2, 3., "World")]
In : pd.DataFrame(data)
Out:
A B C
0 1 2.0 b'Hello'
1 2 3.0 b'World'
In : pd.DataFrame(data, index=['first', 'second'])
Out:
A B C
first 1 2.0 b'Hello'
second 2 3.0 b'World'
In : pd.DataFrame(data, columns=['C', 'A', 'B'])
Out:
C A B
0 b'Hello' 1 2.0
1 b'World' 2 3.0
# list字典的例子
In : d = {'one': [1., 2., 3., 4.],
: 'two': [4., 3., 2., 1.]}
In : pd.DataFrame(d)
Out:
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
In : pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out:
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
情况三:从字典的列表构建Dataframe
Dataframe中一个列是一个Series,也符合在实际使用时,一个列表示一项特征,因此实例化dataframe对象时,会层层解刨数据源中的数据,最外层的字典结构的key会被转换为datafarm的columns(列标签),然后再遇到key,会被转换为index(行标签)。字典的列表也不例外,以下例子中,遇到的第一层字典的key是a,b,c,他们就是所要生成的dataframe的columns。
In : data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In : pd.DataFrame(data2)
Out:
a b c
0 1 2 NaN
1 5 10 20.0
In : pd.DataFrame(data2, index=['first', 'second'])
Out:
a b c
first 1 2 NaN
second 5 10 20.0
In : pd.DataFrame(data2, columns=['a', 'b'])
Out:
a b
0 1 2
1 5 10
情况四:从元组的词典构建DataFrame
通过传递一个元组字典来自动创建一个多索引的框架。这个例子更能体现出上面所说到的从数据源中提取columns和index的规律:一级词典的key作为columns中的元素,二级词典中的key作为index中的元素。
In : pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
: ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
: ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
: ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
: ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
Out:
a b
b a c a b
A B 1.0 4.0 5.0 8.0 10.0
C 2.0 3.0 6.0 7.0 NaN
D NaN NaN NaN NaN 9.0
情况五:使用Dataframe的构造函数
1.DataFrame.from_dict
函数API传送门,顾名思义,构造DataFrame的数据来源为字典类型。它的操作类似于DataFrame构造函数,orient参数表示数据的填充方向,默认为“columns”,表示将字典中的value按照列填充,字典中的key作为列标签,将其设置为“index”时表示横向填充,字典的key作为行标签。
In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]))
Out:
A B
0 1 4
1 2 5
2 3 6
In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]), orient='index')
Out:
0 1 2
A 1 2 3
B 4 5 6
同样的,看过函数的api文档之后,可以知道通过构造函数实例化dataframe对象时也可以传递columns参数,只限于在orient="index"的情况下使用,原因很简单,字典自身肯定是带有key的。
In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]),
: orient='index', columns=['one', 'two', 'three'])
Out:
one two three
A 1 2 3
B 4 5 6
2.DataFrame.from_records
函数API文档传送门,from_records接受元组的列表或ndarray作为参数来构造dataframe。可以将dtype中某个字段指定为要实例化的DataFrame的index。例如下面的例子中,将C列作为index标签。
In : data
Out:
array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In : pd.DataFrame.from_records(data, index='C')
Out:
A B
C
b'Hello' 1 2.0
b'World' 2 3.0
Dataframe常用操作
1.索引、添加和删除
基础的索引操作如下:
操作 | 语法 | 返回结果 |
选择一列 | df[col] | Series |
通过标签选择行 | df.loc[label] | Series |
按整数位置选择行 | df.iloc[loc] | Series |
行切片 | df[5:10] | DataFrame |
通过布尔向量选择行 | df[bool_vec] | DataFrame |
例如,行选择返回一个series,其索引是DataFrame的列:
In : df
Out:
one bar flag foo one_trunc
a 1.0 1.0 False bar 1.0
b 2.0 2.0 False bar 2.0
c 3.0 3.0 True bar NaN
d NaN NaN False bar NaN
In : df.loc['b']
Out:
one 2
bar 2
flag False
foo bar
one_trunc 2
Name: b, dtype: object
In : df.iloc[2]
Out:
one 3
bar 3
flag True
foo bar
one_trunc NaN
Name: c, dtype: object
语义上,可以将DataFrame看作Series的字典。并且获取、设置和删除列的语法与字典的操作相同:
In : df
Out:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In : df['one']
Out:
a 1.0
b 2.0
c 3.0
d NaN
Name: one, dtype: float64
In : df['three'] = df['one'] * df['two']
In : df['flag'] = df['one'] > 2
In : df
Out:
one two three flag
a 1.0 1.0 1.0 False
b 2.0 2.0 4.0 False
c 3.0 3.0 9.0 True
d NaN 4.0 NaN False
删除方面,与dict一样,列可以del和pop操作:
In : del df['two']
In : three = df.pop('three')
In : df
Out:
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
当插入一个标量值时,它会自动地按照列方向传播来填充列,也就是自动按照行进行复制:
In : df['foo'] = 'bar'
In : df
Out:
one flag foo
a 1.0 False bar
b 2.0 False bar
c 3.0 True bar
d NaN False bar
当插入的Series的index和DataFrame的index不同时,它将会贴合DataFrame的index:
In : df['one_trunc'] = df['one'][:2]
In : df
Out:
one flag foo one_trunc
a 1.0 False bar 1.0
b 2.0 False bar 2.0
c 3.0 True bar NaN
d NaN False bar NaN
可以在dataframe中插入ndarray结构的数据,但是它们的长度必须与DataFrame的index的长度匹配。默认情况下,列被插入到末尾,insert函数可用于在列中的特定位置插入。下面的例子中参数的含义:插入位置为1,列名设为‘bar’,数据为使用df[‘one’]一列的数据。
In : df.insert(1, 'bar', df['one'])
In : df
Out:
one bar flag foo one_trunc
a 1.0 1.0 False bar 1.0
b 2.0 2.0 False bar 2.0
c 3.0 3.0 True bar NaN
d NaN NaN False bar NaN
在方法链中分配新列
受dplyr(R语言总高效处理数据的包)的mutate动词的启发,DataFrame有一个assign()方法,允许程序员使用现有的列来创建新列,Assign函数总是返回数据的一个副本,而原始的DataFrame则保持不变。
In : from sklearn.datasets import load_iris
In : iris = pd.DataFrame(load_iris().data, columns=['SepalLength','SepalWidth','PetalLength','PetalWidth'])
In : iris.head()
Out:
SepalLength SepalWidth PetalLength PetalWidth
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
In : (iris.assign(sepal_ratio=iris['SepalWidth'] / iris['SepalLength']).head())
Out:
SepalLength SepalWidth PetalLength PetalWidth sepal_ratio
0 5.1 3.5 1.4 0.2 0.686275
1 4.9 3.0 1.4 0.2 0.612245
2 4.7 3.2 1.3 0.2 0.680851
3 4.6 3.1 1.5 0.2 0.673913
4 5.0 3.6 1.4 0.2 0.720000
在上面的示例中,我们插入了一个预先计算的值。我们还可以传入一个单参数的函数,它的参数将在分配给它的DataFrame上求值,然后将求得的数值插入到dataframe中。
In : iris.assign(sepal_ratio=lambda x: (x['SepalWidth'] / x['SepalLength'])).head() # iris作为参数传给x
Out:
SepalLength SepalWidth PetalLength PetalWidth sepal_ratio
0 5.1 3.5 1.4 0.2 0.686275
1 4.9 3.0 1.4 0.2 0.612245
2 4.7 3.2 1.3 0.2 0.680851
3 4.6 3.1 1.5 0.2 0.673913
4 5.0 3.6 1.4 0.2 0.720000
赋值的函数签名是简单的**kwargs。key是新字段的列名,这些value要么是要插入的值(例如,一个Series或NumPy数组),要么是要在DataFrame上调用的一个参数的函数。assign函数返回原始DataFrame的副本,并向其中插入新值,而不改变原始数据。从Python 3.6开始,保留了**kwargs的顺序。这允许依赖赋值,也就是说,在**kwargs中后面的表达式可以引用前面在相同assign()中创建的列。
In : dfa = pd.DataFrame({"A": [1, 2, 3],
: "B": [4, 5, 6]})
In : dfa.assign(C=lambda x: x['A'] + x['B'],
: D=lambda x: x['A'] + x['C'])
Out:
A B C D
0 1 4 5 6
1 2 5 7 9
2 3 6 9 12
在第二个表达式中,x['C']将指向新创建的列,它等于dfa['A'] + dfa['B']。
数据对齐与算法
DataFrame对象之间的数据对齐会自动对齐列和索引(行标签)。结果对象将具有列和行标签的并集。
In : df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
In : df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
In : df + df2
Out:
A B C D
0 0.045691 -0.014138 1.380871 NaN
1 -0.955398 -1.501007 0.037181 NaN
2 -0.662690 1.534833 -0.859691 NaN
3 -2.452949 1.237274 -0.133712 NaN
4 1.414490 1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
在DataFrame和Series之间执行操作时,默认的行为是对齐DataFrame列上的Series索引,从而按行广播。例如:
In [86]: df - df.iloc[0]
Out[86]:
A B C D
0 0.000000 0.000000 0.000000 0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2 0.253128 0.829678 0.010026 -1.991234
3 -1.311128 0.054325 -1.724913 -1.620544
4 0.573025 1.500742 -0.676070 1.367331
5 -1.741248 0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282 0.000430
7 -0.743894 0.411013 -0.929563 -0.282386
8 -1.194921 1.320690 0.238224 -1.482644
9 2.293786 1.856228 0.773289 -1.446531
在处理时间序列数据的特殊情况下,如果DataFrame索引包含日期,广播将按列显示:
In : index = pd.date_range('1/1/2020', periods=8)
In : df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
In : df
Out:
A B C
2020-01-01 -0.998725 -0.254482 -0.275089
2020-01-02 1.292521 -0.670753 -0.610544
2020-01-03 0.849535 -1.602513 -1.024664
2020-01-04 1.667589 0.008778 -0.311506
2020-01-05 0.959575 -1.265163 -1.019450
2020-01-06 1.292459 0.561263 1.307999
2020-01-07 0.648400 0.869591 0.062901
2020-01-08 -0.421260 0.815654 -2.130895
In : type(df['A'])
Out: pandas.core.series.Series
In : df.sub(df['A'], axis=0) # 不能使用 df - df['A'] 的方式计算列之间的相减
Out:
A B C
2020-01-01 0.0 0.744243 0.723636
2020-01-02 0.0 -1.963274 -1.903065
2020-01-03 0.0 -2.452048 -1.874199
2020-01-04 0.0 -1.658811 -1.979095
2020-01-05 0.0 -2.224738 -1.979025
2020-01-06 0.0 -0.731196 0.015540
2020-01-07 0.0 0.221192 -0.585499
2020-01-08 0.0 1.236914 -1.709635
Dataframe和标量的运算:
In : df * 5 + 2
Out:
A B C
2020-01-01 -2.993623 0.727590 0.624555
2020-01-02 8.462607 -1.353766 -1.052718
2020-01-03 6.247674 -6.012566 -3.123321
2020-01-04 10.337944 2.043890 0.442469
2020-01-05 6.797874 -4.325816 -3.097251
2020-01-06 8.462296 4.806315 8.539996
2020-01-07 5.241999 6.347957 2.314507
2020-01-08 -0.106301 6.078269 -8.654477
In : 1/df
Out[10]:
A B C
2020-01-01 -1.001277 -3.929550 -3.635188
2020-01-02 0.773682 -1.490861 -1.637885
2020-01-03 1.177115 -0.624020 -0.975930
2020-01-04 0.599668 113.920756 -3.210208
2020-01-05 1.042128 -0.790412 -0.980921
2020-01-06 0.773719 1.781696 0.764526
2020-01-07 1.542258 1.149965 15.897902
2020-01-08 -2.373830 1.226010 -0.469286
In : df ** 4
Out[11]:
A B C
2020-01-01 0.994908 4.194002e-03 0.005727
2020-01-02 2.790942 2.024188e-01 0.138953
2020-01-03 0.520864 6.594873e+00 1.102367
2020-01-04 7.733141 5.937294e-09 0.009416
2020-01-05 0.847843 2.562042e+00 1.080100
2020-01-06 2.790406 9.923522e-02 2.927049
2020-01-07 0.176755 5.718222e-01 0.000016
2020-01-08 0.031492 4.426122e-01 20.618097
也可以使用bool运算符进行计算:
In : df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)
In : df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)
In : df1 & df2
Out:
a b
0 False False
1 False True
2 True False
In : df1 | df2
Out:
a b
0 True True
1 True True
2 True True
In : df1 ^ df2
Out:
a b
0 True True
1 True False
2 False True
In : -df1
Out:
a b
0 False True
1 True False
2 False False
转置操作
要想转置,可以访问T属性(也可以是transpose函数),和ndarray操作类似:
In : df[:5].T # 只对前5个进行转置
Out:
2020-01-01 2020-01-02 2020-01-03 2020-01-04 2020-01-05
A -0.998725 1.292521 0.849535 1.667589 0.959575
B -0.254482 -0.670753 -1.602513 0.008778 -1.265163
C -0.275089 -0.610544 -1.024664 -0.311506 -1.019450
Dataframe与NumPy函数的互操作性
假定其中的数据是数字,则可以逐个元素地使用NumPy ufuncs(log,exp,sqrt等)和其他各种NumPy函数,在Series和DataFrame上都不会出现问题:
In : np.exp(df)
Out[13]:
A B C
2020-01-01 0.368349 0.775318 0.759505
2020-01-02 3.641957 0.511323 0.543056
2020-01-03 2.338559 0.201390 0.358917
2020-01-04 5.299375 1.008817 0.732343
2020-01-05 2.610586 0.282193 0.360793
2020-01-06 3.641731 1.752885 3.698766
2020-01-07 1.912478 2.385936 1.064922
2020-01-08 0.656219 2.260653 0.118731
In : np.asarray(df)
Out:
array([[-0.99872467, -0.25448205, -0.27508894],
[ 1.2925213 , -0.67075312, -0.6105436 ],
[ 0.84953475, -1.60251315, -1.02466411],
[ 1.66758887, 0.00877803, -0.31150629],
[ 0.95957478, -1.26516326, -1.01945013],
[ 1.29245922, 0.56126307, 1.30799924],
[ 0.64839989, 0.86959144, 0.06290138],
[-0.42126021, 0.81565377, -2.13089546]])