写在前面的话
在做天文数据处理的过程中,基本上是和类表格数据打交道,因此在处理天文数据的过程中pandas就是自己首选。利用pandas可以很方便的提取出自己想要的行或列的数据,方便自己后面做统计分析。因此,写一篇pandas入门的文章记录一下自己学习pandas的总结。
推荐另外一篇pandas入门文档:10 Minutes to pandas
pandas的功能
- 具备按轴自动或显式数据对齐功能的数据结构。这可以防止许多由于数据未对齐以及来自不同数据源(索引方式不同)的数据而导致的常见错误。
- 集成时间序列功能。
- 既能处理时间序列数据也能处理非时间序列数据的数据结构。
- 数学运算和约简(比如对某个轴求和)可以根据不同的元数据(轴号)执行。
- 灵活处理缺失数据。
- 合并及其他出现在常见数据库(例如基于SQL的)中的关系型运算。
pandas引入约定
import pandas as pd
from pandas import Series,DataFrame
pandas数据结构介绍
要使用pandas,必须先要熟悉它的两个主要数据结构:Series和Data Frame。
Series
Series是一种类似于一维数组的对象,它由一组数据和一组与之相关的数据标签(索引)组成。
由一组数据生产简单的Series
a = [1,3,'hello',4.5]
b =pd.Series(a)
print(b)
0 1
1 3
2 hello
3 4.5
dtype: object
Series的表现形式为:索引在左边,值在右边。由于没有为数据指定索引,默认自动创建一个0到n-1(n为数据的长度)的整数型索引。可以通过Series的values和index属性获取其数组表示形式和索引对象:
b.values
array([1, 3, 'hello', 4.6], dtype=object)
b.index #like range(4)
RangeIndex(start=0, stop=4, step=1)
通过索引可以获取对应的值:
b[2]
'hello'
b[1:3]
1 3
2 hello
dtype: object
b[[1,2]]
1 3
2 hello
dtype: object
自定义索引:
a = [1,3,'hello',4.5]
c = pd.Series(a,index=[c,d,m,n])
print(c)
c 1
d 3
m hello
n 4.6
dtype: object
c['d']
3
c[['m','n']]
m hello
n 4.6
dtype: object
使用Numpy函数或者Numpy的运算,都会保留索引值的链接:
obj =pd.Series([1,5,7,3,2,-3])
print(obj)
0 1
1 5
2 7
3 3
4 2
5 -3
dtype: int64
obj[obj<3]
0 1
4 2
5 -3
dtype: int64
obj + 1
0 2
1 6
2 8
3 4
4 3
5 -2
dtype: int64
可以将Series看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。
print(c)
c 1
d 3
m hello
n 4.6
dtype: object
'm' in c
True
's' in c
False
如果数据被存放在一个Python字典中,可以直接通过这个字典来创建Series。
data = {'Tom':25,'Jerry':23,'Allon':26,'xiaoming':7}
sdata = Series(data)
sdata
Tom 25
Jerry 23
Allon 26
xiaoming 7
dtype: int64
如果只传入一个字典,则结果Series中的索引就是原字典中的键(Key)。当然你也可以传入排好序的字典的键来改变顺序。
name = ['Jerry','xiaoming','Tom','Bob']
obj1 = Series(data,index=name)
print(obj1)
Jerry 23.0
xiaoming 7.0
Tom 25.0
Bob NaN
dtype: float64
由于'Bob'所对应的值找不到,所以就显示NAN(not a number),在pandas中NAN用于表示缺失或者NA值。由于'Allon'不在name中,所以它被从结果中除去。
pandas中的isnull和notnull函数可用于检查缺失的数据:
pd.isnull(obj1)
Jerry False
xiaoming False
Tom False
Bob True
dtype: bool
#Series也有类似的用法,是指已经导入Series函数
obj1.isnull()
Jerry False
xiaoming False
Tom False
Bob True
dtype: bool
Series会根据运算的索引自动对齐数据(类似join操作):
obj2 = Series({'a':4,'d':6,'k':8,'f':7})
obj3 = Series({'d':1,'f':3,'a':6,'n':5})
obj2 + obj3
a 10.0
d 7.0
f 10.0
k NaN
n NaN
dtype: float64
Series的name属性,该属性与pandas其它关键功能关系密切:
obj1
Jerry 23.0
xiaoming 7.0
Tom 25.0
Bob NaN
dtype: float64
obj1.name = 'population'
obj1.index.name = 'name'
print(obj1)
name
Jerry 23.0
xiaoming 7.0
Tom 25.0
Bob NaN
Name: population, dtype: float64
Series的索引值可以通过赋值方式就地修改:
obj2
a 4
d 6
k 8
f 7
dtype: int64
obj2.index = ['e','f','h','m']
obj2
e 4
f 6
h 8
m 7
dtype: int64
DataFrame
DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引。
建DataFrame的办法有很多,最常用的一种是直接传入一个由等长列表或Numpy数组组成的字典:
data1 = {'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] }
frame = pd.DataFrame(data1)
frame
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
我自己常用的构建DataFrame的方法:
f = [8,5,3,7]
m = [15,75,34,52]
fm = pd.DataFrame([f,m])
fm
0 1 2 3
0 8 5 3 7
1 15 75 34 52
没有索引(行or列),DataFrame会自动加上(跟Series一样)。
对于特别大的DataFrame,head函数会选取前五行:
frame.head()
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
如果指定列序列,则DataFrame的列就会按照指定的顺序排列:
pd.DataFrame(data1, columns=['year','pop','state'])
year pop state
0 2000 1.5 Ohio
1 2001 1.7 Ohio
2 2002 3.6 Ohio
3 2001 2.4 Nevada
4 2002 2.9 Nevada
5 2003 3.2 Nevada
通过类似字典标记的方式或属性的方式,可以将DataFrame的某一列获取为一个Series:
frame['pop']
0 1.5
1 1.7
2 3.6
3 2.4
4 2.9
5 3.2
Name: pop, dtype: float64
frame.year
0 2000
1 2001
2 2002
3 2001
4 2002
5 2003
Name: year, dtype: int64
提示:返回的Series拥有原DataFrame相同的索引,而且name属性也被相应的设置好了。
行也可以通过位置或者名称进行获取,比如用loc属性
frame
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
frame.loc[0]
state Ohio
year 2000
pop 1.5
Name: 0, dtype: object
列可以通过赋值的方式进行修改:
frame['pop'] = 2.0
frame
state year pop
0 Ohio 2000 2.0
1 Ohio 2001 2.0
2 Ohio 2002 2.0
3 Nevada 2001 2.0
4 Nevada 2002 2.0
5 Nevada 2003 2.0
将列表或数组赋值给某个列时,其长度必须跟DataFrame的长度相匹配。如果赋值的是一个Series,就会精确匹配DataFrame的索引,所有的空位都将被填上缺失值。
#此例是直接添加某一列,当pop列本身存在时,就是赋值。
frame['pop'] = np.arange(6.)
frame
state year pop
0 Ohio 2000 0.0
1 Ohio 2001 1.0
2 Ohio 2002 2.0
3 Nevada 2001 3.0
4 Nevada 2002 4.0
5 Nevada 2003 5.0
#Series
val = pd.Series([1.2,4.3,5.7], index=[0,3,5])
frame['add'] = val
frame
state year pop add
0 Ohio 2000 0.0 1.2
1 Ohio 2001 1.0 NaN
2 Ohio 2002 2.0 NaN
3 Nevada 2001 3.0 4.3
4 Nevada 2002 4.0 NaN
5 Nevada 2003 5.0 5.7
使用del函数可以删除某一列:
del frame['pop']
frame
state year
0 Ohio 2000
1 Ohio 2001
2 Ohio 2002
3 Nevada 2001
4 Nevada 2002
5 Nevada 2003
另一种常见的数据形式是嵌套字典:
data2 = {'Nevada':{2001:2.4, 2002:2.9},
'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}
frame1 = pd.DataFrame(data2)
frame1
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
Tips:外层字典的键作为列,内层字典的键作为行索引。
对DataFrame进行转值:
frame1.T
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6
设置DataFrame的index和columns的name属性:
frame1.index.name = 'year'
frame1.columns.name = 'state'
frame1
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
可以输入给DataFrame构造器的数据类型:
pandas基本功能
本节介绍处理Series和DataFrame中的数据的基本手段,更详细的pandas库介绍可以看官网文档User_Guide。
重新索引
pandas中的reindex是创建一个新的对象,使得数据符合新的索引。如果某个索引值当前不存在,则填入确实值。
obj4 = pd.Series([4.5,7.2,-5.3,3.6], index=['d','b','a','c'])
obj4
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
obj5 = obj4.reindex(['a','b','c','d','e'])
obj5
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
reindex函数的参数
参数 | 说明 |
---|---|
index | 用作索引的新序列 |
method | 插值(填充)方式,ffill or pad 向前填充值;bfill or backfill 向后填充值 |
fill_value | 引入缺失值时使用的替代值 |
limit | 向前或向后填充时的最大填充量 |
tolerance | 向前或向后填充时,填充不准确匹配项的最大间距(绝对距离) |
level | 在Multilndex的指定级别上匹配简单索引,否则选取其子集 |
copy | 默认为True,无论如何都复制;如果为False,则新旧相等就不复制 |
向前填充:
obj6 = pd.Series(['blue', 'yellow', 'orange'],index=[0,2,4])
obj6
0 blue
2 yellow
4 orange
dtype: object
obj6.reindex(range(6),method='ffill')
0 blue
1 blue
2 yellow
3 yellow
4 orange
5 orange
dtype: object
列可以用columns关键字重新索引:
frame2 = pd.DataFrame(np.arange(9).reshape((3,3),),
index=['a', 'c', 'd'],
columns=['Ohio', 'Texas', 'California'])
frame2
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
states = ['Ohio', 'Utah', 'California']
frame2.reindex(columns=states)
Ohio Utah California
a 0 NaN 2
c 3 NaN 5
d 6 NaN 8
丢弃指定轴上的项drop函数
obj6.drop(2)
0 blue
4 orange
dtype: object
#axis=0 or axis='rows'传递为行(默认);axis=1 or axis='columns'传递为列
frame2.drop('Ohio', axis=1)
Texas California
a 1 2
c 4 5
d 7 8
#当drop函数中的参数设置为inplace=True时,就地修改对象,不会返回新的对象。必须要小心使用inplace,它会销毁所有被删除的数据
索引、选取和过滤
Series索引的工作方式类似于Numpy数组的索引,只不过Series的索引值不只是整数。
obj5
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
#利用标签切片运算与普通的Python的切片不同,其末端是包含的
obj5['a':'c']
a -5.3
b 7.2
c 3.6
dtype: float64
#不用标签索切片时,与Python的切片相同
obj5[:3]
a -5.3
b 7.2
c 3.6
dtype: float64
用一个值或序列对DataFrame进行索引其实就是获取一个或多个列:
frame
state year pop add
0 Ohio 2000 0.0 1.2
1 Ohio 2001 1.0 NaN
2 Ohio 2002 2.0 NaN
3 Nevada 2001 3.0 4.3
4 Nevada 2002 4.0 NaN
5 Nevada 2003 5.0 5.7
frame[['state', 'pop']]
state pop
0 Ohio 0.0
1 Ohio 1.0
2 Ohio 2.0
3 Nevada 3.0
4 Nevada 4.0
5 Nevada 5.0
frame[1:4]
state year pop add
1 Ohio 2001 1.0 NaN
2 Ohio 2002 2.0 NaN
3 Nevada 2001 3.0 4.3
frame[frame['pop']>3]
state year pop add
4 Nevada 2002 4.0 NaN
5 Nevada 2003 5.0 5.7
用loc和iloc进行选
对于DataFrame的行的标签索引,可以使用特殊的标签运算符轴标签loc和整数索引iloc。
frame.loc[3,['state', 'pop']]
state Nevada
pop 3
Name: 3, dtype: object
#iloc可以这样理解:不管有没有设置新的行或列索引,默认的索引都是存在的,而iloc就是提取默认的索引值。
frame.iloc[3,[0,2]]
state Nevada
pop 3
Name: 3, dtype: object
DataFrame数据选取与整合总结
类型 | 说明 |
---|---|
df[val] | 选取单列数据 |
df.loc[val] | 通过标签,选取DataFrame的单行数据 |
df.loc[:,val] | 通过标签,选取单列数据 |
df.loc[,val1, val2] | 通过标签,同时选取行和列对应的数据 |
df.iloc[where] | 通过整数位置,选取单个行数据 |
df.oloc[:, where] | 通过整数位置,选取单个列数据 |
df.iloc[where_i, where_j] | 通过整数位置,同时选取行和列对应的数据 |
df.at[label_i, label_j] | 通过行标签和列标签,选取对应的数据 |
df.iat[i,j] | 通过行和列的位置(整数),选取对应的数据 |
reindex | 通过标签选取行或列 |
get_value,set_value | 通过行和列标签选取数据 |
算术运算和数据对齐
pandas最重要的一个功能是:对不同索引的对象进行算术运算,当数据相加时,如果存在不同索引,则结果的索引是所有数据索引的并集。
s1 = pd.Series([2,5,7,1], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([1,7,9,3,4], index=['a', 'c', 'e', 'f', 'g'])
s1
a 2
c 5
d 7
e 1
dtype: int64
s2
a 1
c 7
e 9
f 3
g 4
dtype: int64
#自动的数据对齐操作在不重叠的索引处引入了NA值,对于DataFrame会同时发生在行和列上
s1 + s2
a 3.0
c 12.0
d NaN
e 10.0
f NaN
g NaN
dtype: float64
在算术方法中填充值
在对不同索引的对象进行算术运算时,我们可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个特殊值(比如0)。此时可以使用算术方法在fill_value传入参数0.
df1 = pd.DataFrame(np.arange(12.).reshape((3,4)),
columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4,5)),
columns=list('abcde'))
df1
a b c d
0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.011.0
df2
a b c d e
0 0.0 1.0 2.0 3.0 4.0
1 5.0 6.0 7.0 8.0 9.0
2 10.011.012.013.014.0
3 15.016.017.018.019.0
df1 + df2
a b c d e
0 0.0 2.0 4.0 6.0 NaN
1 9.0 11.013.015.0NaN
2 18.020.022.024.0NaN
3 NaN NaN NaN NaN NaN
#填充值0
df1.add(df2,fill_value=0)
a b c d e
0 0.0 2.0 4.0 6.0 4.0
1 9.0 11.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
Series和DataFrame算术方法
每个算术方法都有一个副本,以字母r开头,它会翻转参数。两个语句等价。
方法 | 说明 |
---|---|
add | +加 |
sub, rsub | -减 |
div, rdiv | /除 |
floordiv, rfloordiv | //底除 |
mul, rmul | *乘 |
pow, rpow | **指数 |
DataFrame和Series之间的运算
arr = np.arange(12).reshape((3,4))
arr
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
arr[0]
array([0, 1, 2, 3])
#从arr减去arr[0],每一行都会执行这个操作。这叫做广播(broadcasting)
arr - arr[0]
array([[0, 0, 0, 0],
[4, 4, 4, 4],
[8, 8, 8, 8]])
#DataFrame和Series之间的运算差不多也是如此
frame3 = pd.DataFrame(np.arange(12).reshape((4,3)),
columns=list('abc'),
index=list('EMNF'))
frame3
a b c
E 0 1 2
M 3 4 5
N 6 7 8
F 9 10 11
s3 = frame3.iloc[0]
s3
a 0
b 1
c 2
Name: E, dtype: int64
frame3 - s3
a b c
E 0 0 0
M 3 3 3
N 6 6 6
F 9 9 9
如果希望在列上传播,则必须使用算术运算方法。
s4 = frame3['a']
s4
E 0
M 3
N 6
F 9
Name: a, dtype: int64
frame3.sub(s4,axis='index')
a b c
E 0 1 2
M 0 1 2
N 0 1 2
F 0 1 2
函数应用和映射
Numpy的函数也可以用于操作pandas对象:
np.add(frame3,2)
a b c
E 2 3 4
M 5 6 7
N 8 9 10
F 11 12 13
另一个常见的操作是,将函数应用到各列或各行所形成的一维数组上。DataFrame的applay方法即可实现此功能。
frame3
a b c
E 0 1 2
M 3 4 5
N 6 7 8
F 9 10 11
f = lambda x: x.max() - x.min()
frame3.apply(f)
a 9
b 9
c 9
dtype: int64
def f(x):
return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame3.apply(f, axis='columns')
min max
E 0 2
M 3 5
N 6 8
F 9 11
frame4 = pd.DataFrame(np.random.randn(4,3),
columns=list('abc'),
index=list('DEFG'))
frame4
a b c
D 0.279946 0.697885 -0.159838
E -0.890032 0.480782 1.053987
F -0.734350 -0.869877 0.439226
G 1.591106 0.822776 0.350413
#使用applymap函数得到frame4中各个浮点值的格式化字符串
f = lambda x: '%.2f' %x
frame4.applymap(f)
a b c
D 0.28 0.70 -0.16
E -0.89 0.48 1.05
F -0.73 -0.87 0.44
G 1.59 0.82 0.35
结语:有关pandas的入门介绍就写到此,上面的笔记都是自己看《利用Python进行数据分析》第五章节的记录,绝大部分是书中的例题,也有一小部分是自己的添加的。
参考书籍:《利用Python进行数据分析》
2019-06-13