import numpy as np
import pandas as pd
1. 基本的数据结构
1.1 Series
Series 是一维的标记数组,可以容纳任意数据类型,比如整数、字符串、浮点数或者 Python 对象,轴标记则称之为索引(index),其可以通过 s = pd.Series(data, index=index) 来简单地进行创建。
这里,data 可以是一个字典、Numpy 数组或者是一个标量,传进去的 index 是一个轴标记的列表。
# 通过数组来创建
data = np.random.rand(5)
s = pd.Series(data)
print(s)
index = ['a', 'b', 'c', 'd', 'e']
s = pd.Series(data, index=index)
print(s)
index = ['a', 'a', 'c', 'd', 'e']
pd.Series(data, index=index)
0 0.161391
1 0.399114
2 0.239517
3 0.174435
4 0.509980
dtype: float64
a 0.161391
b 0.399114
c 0.239517
d 0.174435
e 0.509980
dtype: float64
a 0.161391
a 0.399114
c 0.239517
d 0.174435
e 0.509980
dtype: float64
如果不指定 index,则索引默认为 [0, 1...n-1]。而且索引并不一定必须是唯一的,只不过当进行一个不支持重复索引的操作时,会抛出一个异常。
# 通过字典来创建
d = {'b': 1, 'a': 0, 'c': 2}
pd.Series(d)
pd.Series(d, index=['a', 'b', 'c', 'd'])
a 0.0
b 1.0
c 2.0
d NaN
dtype: float64
如果不指定 index,则索引默认为字典的键,如果指定了的话,我们就从字典中取出索引对应的数据,缺失的值则默认标记为 NaN。
# 通过标量来创建
index = ['a', 'b', 'c', 'd', 'e']
pd.Series(5, index)
a 5
b 5
c 5
d 5
e 5
dtype: int64
如果通过标量来创建,那么必须指定 index,数值会重复来匹配索引的长度。
print(s[0])
print(s[1:-1])
print(s[s > 0.40])
s.dtype
0.1613914172020462
b 0.399114
c 0.239517
d 0.174435
dtype: float64
e 0.50998
dtype: float64
dtype('float64')
Series 是类数组的,支持一些数组的操作,比如你可以通过切片来访问,也可以查看它的数据类型等。
print(s['a'])
s['e'] = 12
print(s)
'e' in s
0.1613914172020462
a 0.161391
b 0.399114
c 0.239517
d 0.174435
e 12.000000
dtype: float64
True
Series 是类字典的,你可以通过索引得到它对应的值,也可以改变数据等。
print(s * 2)
print(np.exp(s))
s[1:] + s[:-1]
a 0.322783
b 0.798228
c 0.479035
d 0.348871
e 24.000000
dtype: float64
a 1.175145
b 1.490504
c 1.270636
d 1.190574
e 162754.791419
dtype: float64
a NaN
b 0.798228
c 0.479035
d 0.348871
e NaN
dtype: float64
Series 可以像数组一样支持向量化的操作,只不过这里它们会根据索引自动对齐,而不用我们去考虑,在其中一个 Series 中缺失的值会标记为 NaN。
s = pd.Series(np.random.randn(5), name='something')
print(s)
s = s.rename('another thing')
s.name
0 2.088204
1 -0.251980
2 0.191687
3 -0.702280
4 2.679027
Name: something, dtype: float64
'another thing'
Series 还可以有一个名称的属性,我们也可以随时对其进行更改。
1.2 DataFrame
DataFrame 是二维标记的数据结构,它的列可以是不同的类型,可以将它看作是一个电子表格、SQL 表或者是包含 Series 对象的字典。DataFrame 接受很多种不同的输入:包含一维数组、列表、字典或者 Series 的字典;二维数组;另一个 DataFrame 等。同时,也可以选择性地传递索引(index)——行标记和列(columns)——列标记参数。
# 从包含 Series 的字典创建
d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print(df)
df1 = pd.DataFrame(d, index=['d', 'b', 'a'])
print(df1)
df2 = pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
print(df2)
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
two three
d 4.0 NaN
b 2.0 NaN
a 1.0 NaN
结果中的索引将会是不同 Series 的并集,同时我们也可以分别指定索引和列。
# 从数组或者列表创建
d = {'one': [1., 2., 3., 4.],
'two': [4., 3., 2., 1.]}
print(pd.DataFrame(d))
print(pd.DataFrame(d, index=['a', 'b', 'c', 'd']))
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
这时候数组的大小必须相同,如果传递了索引的话,那么索引的长度必须和数组大小一致。
print(df['one'])
df['three'] = df['one'] * df['two']
df['flag'] = df['one'] > 2
print(df)
del df['two']
three = df.pop('three')
print(df)
df['foo'] = 'bar'
df['one_trunc'] = df['one'][:2]
print(df)
a 1.0
b 2.0
c 3.0
d NaN
Name: one, dtype: float64
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
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
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 看作是一个字典来对它的列执行取出、设置和删除操作。当插入一个标量的时候,这个值会填满整列。
2. 10 分钟入门
2.1 查看数据
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
print(df)
A B C D
2013-01-01 -0.894992 1.827753 -0.525777 0.187190
2013-01-02 -0.844346 1.267231 -0.645592 0.919141
2013-01-03 0.796179 -1.399534 0.686902 1.388934
2013-01-04 -0.579014 -0.409705 -0.386375 1.032558
2013-01-05 -1.676227 1.101409 -1.348775 -0.177341
2013-01-06 0.024847 -0.205337 1.801102 0.265285
print(df.head())
print(df.tail(3))
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
one two
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
print(df.index)
df.columns
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
Index(['A', 'B', 'C', 'D'], dtype='object')
# 快速显示数据的统计特性
print(df.describe())
one two
count 3.0 4.000000
mean 2.0 2.500000
std 1.0 1.290994
min 1.0 1.000000
25% 1.5 1.750000
50% 2.0 2.500000
75% 2.5 3.250000
max 3.0 4.000000
# 对列索引降序排列
df = df.sort_index(axis=1, ascending=False)
print(df)
two one
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d 4.0 NaN
# 对 B 列的数值排序
df = df.sort_values(by='B')
print(df)
A B C D
2013-01-03 0.796179 -1.399534 0.686902 1.388934
2013-01-04 -0.579014 -0.409705 -0.386375 1.032558
2013-01-06 0.024847 -0.205337 1.801102 0.265285
2013-01-05 -1.676227 1.101409 -1.348775 -0.177341
2013-01-02 -0.844346 1.267231 -0.645592 0.919141
2013-01-01 -0.894992 1.827753 -0.525777 0.187190
2.2 按照标记选择
print(df.loc[dates[0:1]])
A B C D
2013-01-01 -0.894992 1.827753 -0.525777 0.18719
print(df.loc['20130102':'20130104', ['A', 'B']])
A B
2013-01-03 0.796179 -1.399534
2013-01-04 -0.579014 -0.409705
2013-01-02 -0.844346 1.267231
df.loc[dates[0], 'A']
0.8144031024228705
2.3 按照位置选择
df.iloc[3]
A 0.091327
B -0.570903
C 1.855473
D 1.807595
Name: 2013-01-04 00:00:00, dtype: float64
print(df.iloc[3:5, 0:2])
A B
2013-01-05 -1.676227 1.101409
2013-01-02 -0.844346 1.267231
print(df.iloc[1, 1])
-0.4097050352704689
2.4 布尔索引
print(df[df.A > 0])
A B C D
2013-01-03 0.796179 -1.399534 0.686902 1.388934
2013-01-06 0.024847 -0.205337 1.801102 0.265285
print(df[df > 0])
A B C D
2013-01-03 0.796179 NaN 0.686902 1.388934
2013-01-04 NaN NaN NaN 1.032558
2013-01-06 0.024847 NaN 1.801102 0.265285
2013-01-05 NaN 1.101409 NaN NaN
2013-01-02 NaN 1.267231 NaN 0.919141
2013-01-01 NaN 1.827753 NaN 0.187190
2.5 缺失数据
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E'])
df1.loc[dates[0]:dates[1], 'E'] = 1
print(df1)
A B C D E
2013-01-01 -0.894992 1.827753 -0.525777 0.187190 1.0
2013-01-02 -0.844346 1.267231 -0.645592 0.919141 1.0
2013-01-03 0.796179 -1.399534 0.686902 1.388934 NaN
2013-01-04 -0.579014 -0.409705 -0.386375 1.032558 NaN
print(df1.dropna(how='any')) # 删除任意包含 NaN 的行
A B C D E
2013-01-01 -0.894992 1.827753 -0.525777 0.187190 1.0
2013-01-02 -0.844346 1.267231 -0.645592 0.919141 1.0
print(df1.fillna(value=5)) # 填充缺失数据
A B C D E
2013-01-01 -0.894992 1.827753 -0.525777 0.187190 1.0
2013-01-02 -0.844346 1.267231 -0.645592 0.919141 1.0
2013-01-03 0.796179 -1.399534 0.686902 1.388934 5.0
2013-01-04 -0.579014 -0.409705 -0.386375 1.032558 5.0
print(pd.isna(df1))
A B C D E
2013-01-01 False False False False False
2013-01-02 False False False False False
2013-01-03 False False False False True
2013-01-04 False False False False True
2.6 操作
df.mean()
A 0.493828
B -0.555683
C 1.068909
D -0.675655
dtype: float64
df.mean(1)
2013-01-01 0.725959
2013-01-02 0.317094
2013-01-03 -0.834986
2013-01-04 0.795873
2013-01-05 -0.076024
2013-01-06 -0.430818
Freq: D, dtype: float64
df.apply(lambda x: x.max() - x.min())
A 2.283890
B 2.470330
C 2.813372
D 3.389214
dtype: float64
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
s.str.lower()
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
df2 = pd.DataFrame({'one':['A', 'B'], 'two':['c', 'd']})
print(df2)
df2['one'] = df2['one'].apply(str.lower)
df2['two'] = df2['two'].apply(str.upper)
print(df2)
one two
0 A c
1 B d
one two
0 a C
1 b D
2.7 合并
df = pd.DataFrame(np.random.randn(5, 4))
print(df)
0 1 2 3
0 -0.811113 -0.087698 -1.980786 0.129838
1 -0.083298 -0.803188 0.012908 -1.052246
2 0.534317 0.411549 1.638672 -0.182032
3 0.002322 0.857579 1.716546 -0.531305
4 0.210017 0.156584 -0.487976 -1.230442
pieces = [df[:2], df[2:4], df[4:]]
print(pd.concat(pieces))
0 1 2 3
0 -0.811113 -0.087698 -1.980786 0.129838
1 -0.083298 -0.803188 0.012908 -1.052246
2 0.534317 0.411549 1.638672 -0.182032
3 0.002322 0.857579 1.716546 -0.531305
4 0.210017 0.156584 -0.487976 -1.230442
left = pd.DataFrame({'key': ['val', 'foo', 'bar'], 'lval': [0, 1, 2]})
right = pd.DataFrame({'key': ['foo', 'foo', 'bar', 'end'], 'rval': [4, 3, 5, 6]})
print(left)
print(right)
key lval
0 val 0
1 foo 1
2 bar 2
key rval
0 foo 4
1 foo 3
2 bar 5
3 end 6
print(pd.merge(left, right, on='key')) # 基于 key 这一列合并
key lval rval
0 foo 1 4
1 foo 1 3
2 bar 2 5
print(pd.merge(left, right, how='inner')) # 内连接也就是基于键的交集
key lval rval
0 foo 1 4
1 foo 1 3
2 bar 2 5
print(pd.merge(left, right, how='left')) # 左连接以第一个 DataFrame 为主
key lval rval
0 val 0 NaN
1 foo 1 4.0
2 foo 1 3.0
3 bar 2 5.0
print(pd.merge(left, right, how='right')) # 右连接以第二个 DataFrame 为主
key lval rval
0 foo 1.0 4
1 foo 1.0 3
2 bar 2.0 5
3 end NaN 6
print(pd.merge(left, right, how='outer')) # 外连接也就是基于键的并集
key lval rval
0 val 0.0 NaN
1 foo 1.0 4.0
2 foo 1.0 3.0
3 bar 2.0 5.0
4 end NaN 6.0
3. 一个小例子
name = ['张飞', '关羽', '黄忠', '赵云', '典韦', '典韦']
chinese = [66, 95, 95, 90, 80, 80]
english = [65, 85, 92, 88, 90, 90]
math = [np.nan, 98, 96, 77, 90, 90]
dic = dict({'语文':chinese, '英语':english, '数学':math})
score = pd.DataFrame(dic, index=name)
print(score)
数学 英语 语文
张飞 NaN 65 66
关羽 98.0 85 95
黄忠 96.0 92 95
赵云 77.0 88 90
典韦 90.0 90 80
典韦 90.0 90 80
score = score.drop_duplicates()
score = score.fillna(score['数学'].mean())
print(score)
数学 英语 语文
张飞 90.2 65 66
关羽 98.0 85 95
黄忠 96.0 92 95
赵云 77.0 88 90
典韦 90.0 90 80
def cal_total(df):
df['总分'] = df['英语'] + df['英语'] + df['英语']
return df
#print(cal_total(score))
score = score.apply(cal_total, axis=1)
print(score)
数学 英语 语文 总分
张飞 90.2 65.0 66.0 195.0
关羽 98.0 85.0 95.0 255.0
黄忠 96.0 92.0 95.0 276.0
赵云 77.0 88.0 90.0 264.0
典韦 90.0 90.0 80.0 270.0
score = score.sort_values(by='总分')
print(score)
数学 英语 语文 总分
张飞 90.2 65.0 66.0 195.0
关羽 98.0 85.0 95.0 255.0
赵云 77.0 88.0 90.0 264.0
典韦 90.0 90.0 80.0 270.0
黄忠 96.0 92.0 95.0 276.0