《利用Python进行数据分析》Chapter 5

本章主要介绍pandas数据结构和基本操作。
Numpy适合处理同质型的数值类数组数据,而 pandas是用来处理表格型或异质型数据的,经常和其他数值计算工具,比如NumPy和Scipy,以及可视化工具matplotlib一起使用的。

1.数据结构

1.1 Series
1.1.1 一种一维的数组型对象,包含了一个值序列,并且包含了数据标签,即索引。
obj = pd.Series([4, 5, -5, 3])
obj
0    4
1    5
2   -7
3    3
dtype: int64

默认索引是从0到N-1,可以通过values和index属性分别获得Series的值和索引。

obj.values
array([ 4,  5, -7,  3], dtype=int64)
obj.index
RangeIndex(start=0, stop=4, step=1)

可以对Series设定索引格式。

obj2 = pd.Series([4, 5, -7, 3], index=["d", "b", "a", "c"])
obj2
d    4
b    5
a   -7
c    3
dtype: int64

可以通过标签来进行索引取值

obj2[["b", "d", "c"]]
b    5
d    4
c    3
dtype: int64
1.1.2 可以使用字典生成一个Series
sdata = {
     'Ohio': 35000, "Texas": 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
obj3
Ohio      35000
Texas     71000
Oregon    16000
Utah       5000
dtype: int64

可以将想要生成的索引传递给构造函数来生成符合预期的Series

states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)
obj4
California        NaN #NaN是pandas中标记缺失值或NA值的方式,可以使用isnull和notnull来检查
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64
1.1.3 Series对象自身和其索引都有name属性,可以访问:
obj4.name = 'population'
obj4.index.name = 'state'
obj4

state
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
Name: population, dtype: float64
1.1.4 索引可以通过按位置赋值的方式进行改变:
obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
obj

Bob      4
Steve    5
Jeff    -7
Ryan     3
dtype: int64
1.2 DataFrame

表示矩阵的数据表,包含已排序的列集合,每一列可以是不同的值类型。既有行索引又有列索引,可以看作是一个共享相同索引的Series的字典。
在DataFrame中,数据被存储为一个以上的二维块,尽管DataFrame是二维的,但是可以利用分层索引在其中展现更高维度的数据。

1.2.1 利用包含等长度列表或Numpy数组的字典来创建
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]}
frame = pd.DataFrame(data)
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

可以指定列的顺序来进行显示,如果传的列不在字典中,则会在结果中出现缺失值

pd.DataFrame(data, columns=['year', 'state', 'pop'])

	year	state	pop
0	2000	Ohio	1.5
1	2001	Ohio	1.7
2	2002	Ohio	3.6
3	2001	Nevada	2.4
4	2002	Nevada	2.9
5	2003	Nevada	3.2
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'], index=['one', 'two', 'three', 'four', 'five', 'six'])
frame2

		year	state	pop	debt
one		2000	Ohio	1.5	NaN
two		2001	Ohio	1.7	NaN
three	2002	Ohio	3.6	NaN
four	2001	Nevada	2.4	NaN
five	2002	Nevada	2.9	NaN
six		2003	Nevada	3.2	NaN
1.2.2 DataFrame中的一列,可以按字典型标记或者属性来检索,类型为Series:

注:frame2[column]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。且frame2.column的语法无法创建新的列。

frame2['year']
frame2.year

one      2000
two      2001
three    2002
four     2001
five     2002
six      2003
Name: year, dtype: int64

行通过位置或者特殊属性loc/iloc进行选取:

frame2.loc['three']

year     2002
state    Ohio
pop       3.6
debt      NaN
Name: three, dtype: object
frame2['debt'] = 16.5

创建新列(不能使用frame2.eastern语法)

frame2['eastern'] = frame2.state == 'Ohio' #报警告,只能通过frame2["eastern"]来创建
frame2

		year	state	pop	debt	eastern
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.7	False
six		2003	Nevada	3.2	NaN	False

删除某列使用del方法

del frame2['eastern']
1.2.3 包含字典的嵌套字典可用于创建DataFrame,pandas会将字典的键作为列,将内部字典的键作为行索引:
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
2001	2.4		1.7
2002	2.9		3.6
2000	NaN		1.5

frame3.T #支持转置操作
		2001	2002	2000
Nevada	2.4		2.9		NaN
Ohio	1.7		3.6		1.5
1.2.4 包含Series的字典也可以用于创建DataFrame:
pdata = {
     'Ohio': frame3['Ohio'][:-1],
        'Nevada': frame3["Nevada"][:2]}
pd.DataFrame(pdata)

		Ohio	Nevada
2001	1.7		2.4
2002	3.6		2.9

DataFrame的索引和列拥有name属性:

frame3.index.name = 'year'
frame3.columns.name = 'state'
frame3

state	Nevada	Ohio
year		
2001	2.4		1.7
2002	2.9		3.6
2000	NaN		1.5
1.3 索引对象

索引对象是不可变的,这保证了数据结构在分享索引对象时更为安全,但是数据结构的索引是可以通过命名修改的。
pandas索引对象可以包含重复标签,这点与Python集合不同,根据重复标签进行筛选会选取所有重复标签对应的数据。

2. 基本功能

Series或DataFrame中数据交互的基础机制。

2.1 重建索引
2.1.1 reindex方法是pandas对象的重要方法,用于创建一个符合新索引的新对象

a. Series调用reindex方法时,会将数据按照新的索引进行排列,如果某个索引值之前不存在,则引入缺失值。

obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=["d", "b", "a", "c"])
obj
d    4.5
b    7.2
a   -5.3
c    3.6
dtype: float64

obj2 = obj.reindex(["a", "b", "c", "d", "e"])
obj2
a   -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64

b. 对于顺序数据,比如时间序列,在重建索引时可以进行插值或填值。对metho的可选参数ffill表示重建所以时插值 ,并且向前填充:

obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3
0      blue
2    purple
4    yellow
dtype: object

obj3.reindex(range(6), method='ffill')
0      blue
1      blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object

c. DataFrame中,reindex不仅能改变行索引,还可以对列索引进行更改,也可以同时改变两者。

frame = pd.DataFrame(np.arange(9).reshape((3,3)),
                     index = ['a', 'c', 'd'],
                     columns = ['Ohio', 'Texas', 'California'])
frame
	Ohio	Texas	California
a	0		1		2
c	3		4		5
d	6		7		8
frame2 = frame.reindex(['a', 'b', 'c', 'd']) #更改行索引
frame2
	Ohio	Texas	California
a	0.0		1.0		2.0
b	NaN		NaN		NaN
c	3.0		4.0		5.0
d	6.0		7.0		8.0
states = ['Texas', 'Utah', "california"]
frame3 = frame.reindex(columns = states) #更改列索引,通过关键字重建索引
frame3
	Texas	Utah	california
a	1		NaN		NaN
c	4		NaN		NaN
d	7		NaN		NaN
2.2 轴向上删除项目
2.2.1 Series的drop方法通过索引值选取删除的数据
obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
obj
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64

new_obj = obj.drop(['c', 'd'], inplace=True) #inplace属性会清除被删除的数据。
new_obj
a    0.0
b    1.0
e    4.0
dtype: float64
2.2.2 在DataFrame中,索引值可以从轴向上删除,默认为行索引,删除列索引时需要指定axis参数
frame = pd.DataFrame(np.arange(9).reshape((3,3)),
                     index = ['a', 'c', 'd'],
                     columns = ['Ohio', 'Texas', 'California'])
frame
	Ohio	Texas	California
a	0		1		2
c	3		4		5
d	6		7		8
frame.drop(["a", "d"]) #删除行索引
	Ohio	Texas	California
c	3		4		5
frame.drop('Ohio', axis=1) #删除列索引
	Texas	California
a	1		2
c	4		5
d	7		8
2.3 索引、选择与过滤
2.3.1 Series的索引与Numpy数组索引功能类似,不过Series的索引也可以不是整数。(Numpy中的神奇索引可以使用整数)

a. 数字型索引遵循前闭后开

obj = pd.Series(np.arange(4.), index=["a", "b", "c", "d"])
obj
a    0.0
b    1.0
c    2.0
d    3.0
dtype: float64

obj[1:3] #前闭后开
b    1.0
c    2.0
dtype: float64

b. 使用索引值来进行索引,则是前闭后闭,这点与普通的Python切片不同。

obj['b':'d'] #前闭后闭

b    1.0
c    2.0
d    3.0
dtype: float64
2.3.2 DataFrmae的索引,如果传入的是切片,则是根据行进行索引,如果传入的是单个元素或者列表,则是对列进行索引
frame1 = pd.DataFrame(np.arange(16).reshape(4,4), index=list('abcd'), columns=list('bdac'))
frame1
	b	d	a	c
a	0	1	2	3
b	4	5	6	7
c	8	9	10	11
d	12	13	14	15

a. 对行索引:

frame1[:2] #前闭后开
	b	d	a	c
a	0	1	2	3
b	4	5	6	7

frame1["b":"d"] #前闭后闭
	b	d	a	c
b	4	5	6	7
c	8	9	10	11
d	12	13	14	15

b. 对列索引:

frame1[['a']]
	a
a	2
b	6
c	10
d	14
frame1[['b', 'd']] #不支持对应的整数索引,但是iloc可以使用整数来索引
	b	d
a	0	1
b	4	5
c	8	9
d	12	13

c. 布尔值索引:

frame1[frame1["a"] > 5] #根据布尔值索引
	b	d	a	c
b	4	5	6	7
c	8	9	10	11
d	12	13	14	15

frame1[frame1 < 5] = 0
frame1
	b	d	a	c
a	0	0	0	0
b	0	5	6	7
c	8	9	10	11
d	12	13	14	15
2.3.3 使用loc和iloc选择数据

a. loc使用轴标签选取数据

data = pd.DataFrame(np.arange(16).reshape((4,4)),
                     index = ['a',"b", 'c', 'd'],
                     columns = ['one', 'two', 'three', 'four'])
data
	one	two	three	four
a	0	1	2		3
b	4	5	6		7
c	8	9	10		11
d	12	13	14		15

data.loc['a']
one      0
two      1
three    2
four     3
Name: a, dtype: int32

data.loc[['b','a']] #可以根据传入的索引值顺序来设置返回的数据顺序
	one	two	three	four
b	4	5	6		7
a	0	1	2		3

data.loc[['a', 'b'], ['two', 'three']] #前面的参数是行索引,后面的为列索引
	two	three
a	1	2
b	5	6
data.loc[:, ['three', 'two']] #与data[['three', 'two']]返回相同数据
	three	two
a	2		1
b	6		5
c	10		9
d	14		13
data.loc[:'c', :'two'] #支持切片,前闭后闭
	one	two
a	0	1
b	4	5
c	8	9

b. iloc使用整数标签选取数据

data.iloc[2]
one       8
two       9
three    10
four     11
Name: c, dtype: int32

data.iloc[[1, 3]]
	one	two	three	four
b	4	5	6		7
d	12	13	14		15
data.iloc[1, 3] #注意与上一条语句区别
7

data.iloc[[1, 2], [3, 0, 1]]
	four	one	two
b	7		4	5
c	11		8	9

data.iloc[:, :3] #支持切片,前闭后开
	one	two	three
a	0	1	2
b	4	5	6
c	8	9	10
d	12	13	14
2.3.4 整数索引

假设有一个索引,它包含了0、1、2,这样推断用户所需的索引位置可能会有歧义:

ser = pd.Series(np.arange(3))
ser
0    0
1    1
2    2
dtype: int32
ser[:1]
0    0
dtype: int32
ser.loc[:1] #注意这个1是索引值的1,而不是整数索引的1,所以是前闭后闭
0    0
1    1
dtype: int32
ser.iloc[:1]
0    0
dtype: int32
2.4 算术和数据对齐
2.4.1 DataFrame对象相加时,如果存在某个索引对不相同,则返回结果的索引将时索引对的并集,类似于索引标签的自动外连接(outer join)。
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
2.4.2 两个不同的索引化对象之间进行算术操作时,可以使用特殊填充值。
df1.add(df2, fill_value=0) #将不存在的索引对象填充为0然后进行add操作
	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

a. 算术方法中每一个都有一个以r开头的副本,这些副本方法的参数是翻转的。
add radd / sub rsub / div rdiv / floordiv rfloordiv / mul rmul / pow rpow
b. DataFrame和Series间的算术操作与Numpy中不同维度数组间的操作类似,遵循广播机制。

2.5 函数应用和映射
2.5.1 Numpy的通用函数(逐元素数组方法)对pandas对象也有效。
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), 
		index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame
			b			d			e
Utah	0.806455	-0.135015	0.500107
Ohio	0.729342	-0.415839	-1.409110
Texas	-2.895623	-0.815162	0.777856
Oregon	-1.499056	-0.714246	-1.336805
np.abs(frame)
			b			d			e
Utah	0.806455	0.135015	0.500107
Ohio	0.729342	0.415839	1.409110
Texas	2.895623	0.815162	0.777856
Oregon	1.499056	0.714246	1.336805
2.5.2 apply方法可将函数应用到一行或一列的一维数组上
f = lambda x: x.max() - x.min()
frame.apply(f)
b    3.702078
d    0.680147
e    2.186966
dtype: float64

frame.apply(f, axis=1)
Utah      0.941470
Ohio      2.138453
Texas     3.673478
Oregon    0.784810
dtype: float64
def f(x):
    return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame.apply(f)
		b			d			e
min	-2.895623	-0.815162	-1.409110
max	0.806455	-0.135015	0.777856

逐元素的Python函数也可以使用,可以使用applymap方法:

format_f = lambda x: '%.2f' % x
frame.applymap(format_f)
			b		d		e
Utah	0.81	-0.14	0.50
Ohio	0.73	-0.42	-1.41
Texas	-2.90	-0.82	0.78
Oregon	-1.50	-0.71	-1.34

frame['e'].map(format_f) #对于Series则有对应的map方法也可以使用
Utah       0.50
Ohio      -1.41
Texas      0.78
Oregon    -1.34
Name: e, dtype: object
2.6 排序和排名
2.6.1 排序

根据某些准则进行排序是另一个重要的内建操作。
a. 如需按行或者列索引进行排序,可以使用sort_index方法,该方法返回一个新的、排序好的对象:

frame = pd.DataFrame(np.arange(8).reshape((2,4)), index=['three', 'one'], columns=list('dabc'))
frame
		d	a	b	c
three	0	1	2	3
one		4	5	6	7
frame.sort_index() #按行索引排序
		d	a	b	c
one		4	5	6	7
three	0	1	2	3
frame.sort_index(axis=1) #按列索引排序
		a	b	c	d
three	1	2	3	0
one		5	6	7	4
frame.sort_index(axis=1, ascending=False) #降序排列
		d	c	b	a
three	0	3	2	1
one		4	7	6	5

b. 根据Series的值进行排序,使用sort_values方法:

obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj
0    4.0
1    NaN
2    7.0
3    NaN
4   -3.0
5    2.0
dtype: float64
obj.sort_values() #默认情况下,所有的缺失值都会被排序至尾部
4   -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

c. DataFrame排序时可以使用一列或多列作为排序键:

frame = pd.DataFrame({
     'b': [4, 7, -3, 2],
                     'a': [0, 1, 0, 1]})
frame
	b	a
0	4	0
1	7	1
2	-3	0
3	2	1
frame.sort_values('b')
	b	a
2	-3	0
3	2	1
0	4	0
1	7	1
frame.sort_values(by=['a', 'b'])
	b	a
2	-3	0
0	4	0
3	2	1
1	7	1
2.6.2 排名

排名是指数组从1到有效数据点总数分配名次的操作。Series和DataFrame的rank方法是实现排名的方法,且支持多种不同的排名方式。

a = pd.Series([1,2,2,2,3,5,6,3,4]) 
rank_a = pd.DataFrame() 
rank_a['base'] = a 
rank_a['average'] = a.rank(method='average') # 默认,在每个组中分配平均排名(组,相同的值就是一个组)
rank_a['min'] = a.rank(method='min') # 对整个组使用最小排名
rank_a['first'] = a.rank(method='first')  # 按照值在数据中出现的次序分配排名
rank_a['max'] = a.rank(method='max')  # 对整个组使用最大排名
rank_a['dense'] = a.rank(method='dense') # 类似于‘min’,但是组间排名总是增加1,而不是相等元素的数量
rank_a

	base	average	min	first	max	dense
0	1		1.0		1.0	1.0		1.0	1.0
1	2		3.0		2.0	2.0		4.0	2.0
2	2		3.0		2.0	3.0		4.0	2.0
3	2		3.0		2.0	4.0		4.0	2.0
4	3		5.5		5.0	5.0		6.0	3.0
5	5		8.0		8.0	8.0		8.0	5.0
6	6		9.0		9.0	9.0		9.0	6.0
7	3		5.5		5.0	6.0		6.0	3.0
8	4		7.0		7.0	7.0		7.0	4.0
2.6.3 重复索引的情况

索引的is_unique属性可以查看数据标签是否唯一:

obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
obj
a    0
a    1
b    2
b    3
c    4
dtype: int64
obj.index.is_unique
False
2.7 描述性统计的概述与计算

pandas装配的常用数学、统计学方法的集合可以从DataFrame的行或列中抽取一个Series或一系列值的单个值(如总和或平均值)。

df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]],
                 index=list('abcd'), columns=['one', 'two'])
df
	one		two
a	1.40	NaN
b	7.10	-4.5
c	NaN		NaN
d	0.75	-1.3
df.mean()
one    3.083333 # (1.40+7.10+0.75)/3
two   -2.900000
dtype: float64
df.mean(axis=1) #除非整行或整列都是NA,否则NA值是被自动排除的。
a    1.400
b    1.300
c      NaN
d   -0.275
dtype: float64

a. 可以通过skipna来实现不排除NA值:

df.mean(skipna=False)
one   NaN
two   NaN
dtype: float64
df.mean(axis=1, skipna=False)
a      NaN
b    1.300
c      NaN
d   -0.275
dtype: float64

b. 常用描述性统计和汇总统计

df.cumsum()
	one		two
a	1.40	NaN
b	8.50	-4.5
c	NaN		NaN
d	9.25	-5.8
df.describe()
			one			two
count	3.000000	2.000000
mean	3.083333	-2.900000
std		3.493685	2.262742
min		0.750000	-4.500000
25%		1.075000	-3.700000
50%		1.400000	-2.900000
75%		4.250000	-2.100000
max		7.100000	-1.300000

c. 相关性和协方差
Series的corr方法计算的是两个Series中重叠的、非NA的、按索引对齐的值的相关性。cov是协方差。
DataFrame的corr和cov方法会分别以DataFrame的形式返回相关性和协方差矩阵。

returns  = price.pct_change()
returns.tail()
			AAPL		IBM			MSFT		GOOG
Date				
2020-02-10	0.004750	0.006649	0.026157	0.019909
2020-02-11	-0.006033	-0.006152	-0.022575	0.000073
2020-02-12	0.023748	0.011923	0.001464	0.006283
2020-02-13	-0.007121	-0.006439	-0.005414	-0.002378
2020-02-14	0.000246	-0.023394	0.008927	0.004014

returns['MSFT'].corr(returns['IBM'])
0.4675467304315449
returns['MSFT'].cov(returns['IBM'])
8.800905589421394e-05
returns.corr()
		AAPL		IBM			MSFT		GOOG
AAPL	1.000000	0.387745	0.584929	0.531989
IBM		0.387745	1.000000	0.467547	0.404837
MSFT	0.584929	0.467547	1.000000	0.670882
GOOG	0.531989	0.404837	0.670882	1.000000
returns.cov()
		AAPL		IBM			MSFT		GOOG
AAPL	0.000242	0.000079	0.000131	0.000125
IBM		0.000079	0.000170	0.000088	0.000080
MSFT	0.000131	0.000088	0.000208	0.000146
GOOG	0.000125	0.000080	0.000146	0.000227

DataFrame的corrwith方法:

returns.corrwith(returns.IBM)
AAPL    0.387745
IBM     1.000000
MSFT    0.467547
GOOG    0.404837
dtype: float64

d. 唯一值、计数和成员属性
常用方法:isin / match / unique / value_counts

data = pd.DataFrame({
     'Qu1': [1, 3, 4, 3, 4],
                    'Qu2': [2, 3, 1, 2, 3],
                    'Qu3': [1, 5, 2, 4, 4]})
data
	Qu1	Qu2	Qu3
0	1	2	1
1	3	3	5
2	4	1	2
3	3	2	4
4	4	3	4
result = data.apply(pd.value_counts) 
result # 行标签是所有列中出现的不同值,数值则是这些不同的值在每个列中出现的次数
	Qu1	Qu2	Qu3
1	1.0	1.0	1.0
2	NaN	2.0	1.0
3	2.0	2.0	NaN
4	2.0	NaN	2.0
5	NaN	NaN	1.0
result1 = data.apply(pd.value_counts).fillna(0)
result1
	Qu1	Qu2	Qu3
1	1.0	1.0	1.0
2	0.0	2.0	1.0
3	2.0	2.0	0.0
4	2.0	0.0	2.0
5	0.0	0.0	1.0

你可能感兴趣的:(Python-数据分析)