利用Python进行数据分析(第三版)--第5章Pandas入门

Pandas可使数据清洗和分析工作更快,更简单。其与NumPy最大的区别就是Pandas是专门为处理表格和异构数据设计(不同的数据类型)的

以下将默认已导入pandas

import numpy as np
import pandas as pd
from pandas import Series,DataFrame

5.1 Pandas的数据结构介绍

5.1.1 Series

Series类似于一维数组,它由一组数据以及一组与之相关的索引(为了不混淆,我只使用这一个词,书里面也叫标签)组成

obj = pd.Series([-1, 4, 9])
#左边为索引,右边为索引对应的数据元素
obj
#0   -1
#1    4
#2    9
#dtype: int64

可以在创建的时候顺便指定索引

obj = pd.Series([4, 7, 3, 2],index = ["a","c","b","d"])

obj
#a    4
#c    7
#b    3
#d    2
#dtype: int64

通过索引选取一个或多个值就不写了,就是在方块号[ ]中指定索引名儿,例如[ ["c","d"] ]

使用NumPy函数或类似NumPy的运算,都会保留索引值的链接

obj[obj > 3]
#a    4
#c    7
#dtype: int64

obj * 2
#a     8
#c    14
#b     6
#d     4
#dtype: int64

np.exp(obj)
#a      54.598150
#c    1096.633158
#b      20.085537
#d       7.389056
#dtype: float64

可以将Series看作固定长度的有序字典,因为其索引值也是对数据的映射。可以传入一个字典创建Series,后面的DataFrame也可以这样创建

sdata = {"Ohio": 35000, "Texas": 71000, "Oregon": 16000, "Utah": 5000}

obj2 = pd.Series(sdata)

obj2
#Ohio      35000
#Texas     71000
#Oregon    16000
#Utah       5000
#dtype: int64

我到这已经开始怀疑我为什么不去手册学Pandas什么的,可能我更偏向看书的方式吧,写这个类似笔记的一些东西也让我能回顾一遍不白看

可以单独创建一个列表来决定已存在的Series中索引的顺序,而新建的列表里出现了Series里没有的索引,则对应的值为空值

states = ["California", "Ohio", "Oregon","Utah"]

obj3 = pd.Series(sdata, index = states)
#标签列表写了几个,最后创建的Series就有几个项
obj3
#California        NaN
#Ohio          35000.0
#Oregon        16000.0
#Utah           5000.0
#dtype: float64

Series在算术运算中能自动对齐索引:

obj2 + obj3
#就是一个Series有的另一个没有,但结果也会显示出来,但值不管原值是多少,结构都是空值
#California        NaN
#Ohio          70000.0
#Oregon        32000.0
#Texas             NaN
#Utah          10000.0
#dtype: float64

5.1.2 DataFrame

DataFrame是矩形的数据表,它含有一组有序且有命名的列,每一列可以是不同的数据类型(异构),可以先把DataFrame看成个二维数组

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

可以通过frame["State"]可以获取相关列的Series包括行索引,就是上面的0-5

通过iloc和loc属性,也可以用来获取行

frame2.loc[1]
#state    Ohio
#year     2001
#pop       1.7
#Name: 1, dtype: object

frame2.iloc[2]
#state    Ohio
#year     2002
#pop       3.6
#Name: 2, dtype: object

将列表或数组赋值给某个列时,其长度必须跟DataFrame的长度相匹配。如果赋值一个Series,它的索引会精确匹配DataFrame里对应的索引,空缺的地方填上空值:

#为不存在的列会创建一个新的列
val = pd.Series([-1.2, -1.5, -1.7], index = [0, 1, 2])

frame["debt"] = val
frame
	state	year	pop	debt
0	Ohio	2000	1.5	-1.2
1	Ohio	2001	1.7	-1.5
2	Ohio	2002	3.6	-1.7
3	Nevada	2001	2.4	NaN
4	Nevada	2002	2.9	NaN
5	Nevada	2003	3.2	NaN
#这里可以看出来,val里的索引对应上了DataFrame里的行索引,然后缺的部分用空值

可以用del方法删除列,方法和Python中del的使用方法一样

嵌套字典的字典,外层字典的键为列名,内层键解释为行名,不同长度则会有空值填上

#pandas将外层字典的键值解释为列索引,内层字典键值解释为行索引
populations = {"Ohio" : {2000:1.5, 2001:1.7, 2002:3.6},
              "Nevada" : {2001:2.4, 2002:2.9}}

frame3 = pd.DataFrame(populations)

frame3
	    Ohio	Nevada
2000	1.5	    NaN
2001	1.7	    2.4
2002	3.6	    2.9
#frame3["Nevada"][2000]的值为NaN

#可以这样通过一个DataFrame创建另一个
pdata = {"Ohio": frame3["Ohio"][:-1],
        "Nevada": frame3["Nevada"][:2]}

pd.DataFrame(pdata)
	    Ohio	Nevada
2000	1.5	    NaN
2001	1.7	    2.4

5.1.3索引对象

与Python集合不同,pandas的索引可以重复

pd.Index(["foo", "foo", "bar", "bar"])

这块我觉得没那么重要,如果以后需要用到到时候再去搜搜

5.2 基本功能

5.2.1 重建索引

pandas对象的一个重要方法是 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"])
#要是有个索引不存在,则会用空值补上,这里对应着"e"
obj2
#a   -5.3
#b    7.2
#c    3.6
#d    4.5
#e    NaN
#dtype: float64

使用 ffill 可以向前填充,这个方法在处理时间序列会用到,我还没学到我不知道这是什么

我的理解是,当行索引之间存在空缺,这里对应着1,3以及指定长度后新增的5,然后向大值延申索引对应的值,直到碰到下一个索引

obj3 = pd.Series(["blue", "purple", "yellow"], index = [0, 2, 4])

obj3
#0      blue
#2    purple
#4    yellow
#dtype: object

obj3.reindex(np.arange(6), method = "ffill")
#0      blue
#1      blue
#2    purple
#3    purple
#4    yellow
#5    yellow
#dtype: object

DataFrame用 reindex 改索引差不多,通常使用loc运算符重建索引

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.loc[["a"],["California"]]
    California
a	2

重建索引就类似新建视图,索引这类操作大差不差的,然后对索引修改的操作会返给原数据上

5.2.2 删除指定轴上的项

对于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")

new_obj
#a    0.0
#b    1.0
#d    3.0
#e    4.0
#dtype: float64

DataFrame操作差不多:

data = pd.DataFrame(np.arange(16).reshape((4, 4)),                   
                    index=["Ohio", "Colorado", "Utah", "New York"],
                    columns=["one", "two", "three", "four"])
data
	        one	two	three	four
Ohio	    0	1	2	    3
Colorado	4	5	6	    7
Utah	    8	9	10	    11
New York	12	13	14	    15

data.drop(index = ["Colorado","Ohio"])
	        one	two	three	four
Utah	    8	9	10	    11
New York	12	13	14	    15

若删除列的话,drop()内写columns = [“ ”,“ ”]就可

5.2.3索引、选取、过滤

通常使用loc来选取索引值:

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.loc[["b", "a", "d"]]
#b    1.0
#a    0.0
#d    3.0
#dtype: float64
#这里与重建索引不同,若给的索引值包括原Series对象没有的索引,会报错

注意,当你索引里没有数字的时候,还向loc传入整数,则loc会失效:

obj2 = pd.Series([1, 2, 3], index=["a", "b", "c"])

obj2.loc[[0,1]]
#报错

iloc运算符则是无论原索引里是否包含整数,都可以传整数:

obj.iloc[[0, 1, 2]]
#a    0.0
#b    1.0
#c    2.0
#dtype: float64

ioc的切片是包含末端的,这是与Python列表不同的地方

DataFrame直接用[]索引的话,传列名会给你包含所有行以及你要求的列。不能传数字,但可以传[:]来选取行的切片

data = pd.DataFrame(np.arange(16).reshape((4, 4)),                   
                    index=["Ohio", "Colorado", "Utah", "New York"],
                    columns=["one", "two", "three", "four"])
data
	        one	two	three	four
Ohio	    0	1	2	    3
Colorado	4	5	6	    7
Utah	    8	9	10	    11
New York	12	13	14	    15

data[:2]
	        one	two	three	four
Ohio	    0	1	2	    3
Colorado	4	5	6	    7

loc与iloc与Series也差不多,要同时选取行和列则在[ ]内使用“,”分开,前面行索引,后面列索引,使用iloc方法单传一个数字和列表则优先选行

data.iloc[[2, 1]]
#这里穿了个列表,选了相应的行,若传入[[2, 1],1]则会选取Utah和Colorado行的two列
	        one	two	three	four
Utah	    8	9	10	    11
Colorado	4	5	6	    7

整数索引中的陷阱,总之说的就是最好用loc和iloc来选取数据,这样清晰且不容易出错

链式索引中的陷阱,就是你想给某一列的某一行的那个位置赋值的问题,在一个[ ]内解决,不要写两个[ ]

5.2.4算数运算和数据对齐

对象相加,若存在不同的索引对,则结果中是索引对的并集(也就是包括了两方差集的索引),但对应的结果为空值

s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=["a", "c", "d", "e"])
#a    7.3
#c   -2.5
#d    3.4
#e    1.5
#dtype: float64

s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],               
                index=["a", "c", "e", "f", "g"])
#a   -2.1
#c    3.6
#e   -1.5
#f    4.0
#g    3.1
#dtype: float64

s1 + s2
#a    5.2
#c    1.1
#d    NaN
#e    0.0
#f    NaN
#g    NaN
#dtype: float64

对于DataFrame也是同理,补上行和列中的不同索引,并对应处为NaN

带有填充值的算术方法

当不同的索引对象进行算数运算时,如果不想补充的值为空值,也可以替换:

df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
                    columns=list("abcd"))
df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
                    columns=list("abcde"))

df2.loc[1,"b"] = np.nan

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.0	11.0

df2
    a	    b	    c	    d	    e
0	0.0	    1.0	    2.0	    3.0	    4.0
1	5.0	    NaN	    7.0	    8.0	    9.0
2	10.0	11.0	12.0	13.0	14.0
3	15.0	16.0	17.0	18.0	19.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	    5.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
#对于不重叠的部分例如第3行以及e列,原本应为NaN,这里替换为原值了

DataFrame和Series间的运算

DataFrame与Series之间的算数运算会将Series的索引匹配到DataFrame的列,然后向下传播

frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
   .....:                      columns=list("bde"),
   .....:                      index=["Utah", "Ohio", "Texas", "Oregon"])

series = frame.iloc[0]

frame
	    b	    d	    e
Utah	0.0	    1.0	    2.0
Ohio	3.0	    4.0	    5.0
Texas	6.0	    7.0	    8.0
Oregon	9.0	    10.0	11.0

series
#b    0.0
#d    1.0
#e    2.0
#Name: Utah, dtype: float64

frame - series
	    b	    d	    e
Utah	0.0	    0.0	    0.0
Ohio	3.0	    3.0	    3.0
Texas	6.0	    6.0	    6.0
Oregon	9.0	    9.0	    9.0

以上例子中,series中的b对应frame的b,d对应frame的d,e也一样,然后对应去做减法

要比配行广播列则 series = frame["b"],然后可以去实验看看,最后结果是每列元素受到同样的运算而改变了

另一种比较抽象不好理解的方法是使用.apply(),这是将函数操作应用到各行或各列的一维数组

def f1(x):    
    return x.max() - x.min()

frame.apply(f1)
#b    9.0
#d    9.0
#e    9.0
#dtype: float64

frame.apply(f1, axis = "column")
#Utah      2.0
#Ohio      2.0
#Texas     2.0
#Oregon    2.0
#dtype: float64

这种方法还有其他用法,但比较抽象就不写了

5.2.6排序和排名

这里主要说明对于行或列进行排序,使用sort_index其返回一个排序好的对象

obj = pd.Series(np.arange(4), index=["d", "a", "b", "c"])

obj
#d    0
#a    1
#b    2
#c    3
#dtype: int32

obj.sort_index()
#a    1
#b    2
#c    3
#d    0
#dtype: int32

对于DataFrame,可以根据任意一个轴上的索引进行排序:

frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
                    index=["three", "one"],
                    columns=["d", "a", "b", "c"])

frame
	    d	a	b	c
three	0	1	2	3
one	    4	5	6	7

frame.sort_index(axis = "columns")
	    a	b	c	d
three	1	2	3	0
one	    5	6	7	4

若要降序排序,则添加属性ascending = False

若要不按索引,而是用值排序,使用sort_values()方法:

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(["a","b"])
	b	a
2	-3	0
0	4	0
3	2	1
1	7	1

frame.sort_values(["b","a"])
	b	a
2	-3	0
3	2	1
0	4	0
1	7	1

从上述例子可以看出,当传入多个列,先按先传入的列排序,再尽可能排序第二个列,这个说法应该是不准确的,但大概是这样

ran()方法默认是 为各组分配平均排名 的方式来破坏平级关系

obj = pd.Series([7, -5, 7, 4, 2, 0, 4])

obj
#0    7
#1   -5
#2    7
#3    4
#4    2
#5    0
#6    4
#dtype: int64

obj.rank()
#0    6.5
#1    1.0
#2    6.5
#3    4.5
#4    3.0
#5    2.0
#6    4.5
#dtype: float64

观看上面的排序结果,因为 3 和 6 处的索引对应的值相同,所以取平均值。可以理解为,在一场比赛中,有两人成绩相同,比赛采取不加时或重比的方式,故这两个人最终得到相同的排名。

5.2.7带有重复标签的轴索引

Series中若有重复索引,则选取这个索引时,会传回一个Series,若选取不重复索引,就返回标量值。

DataFrame同理

5.3描述性统计的汇总和计算

pandas对象拥有一组常用的数学和统计方法,Series和DataFrame都内置有处理缺失数据(NaN)的功能

说白了这一小节就是讲类似sum,imax,cunsum等方法的用法。这个其实有了前面对于Series和DataFrame的了解,可以知道,这些操作也可以应用到指定行或列上。

其实感觉很多都是重复的东西,看多了也能知道宏观上操作都差不多,比如第四章的NumPy的索引操作,和Pandas的Series、DataFrame的索引操作差不多。

5.3.2唯一值、计数以及成员属性

.value_counts(),用于生成Series中唯一值的数组,功能和 set() 提取列表不重复值一样

obj = pd.Series(["c", "a", "d", "a", "a", "b", "b", "c", "c"])

obj
#0    c
#1    a
#2    d
#3    a
#4    a
#5    b
#6    b
#7    c
#8    c
#dtype: object

obj.value_counts()
#c    3
#a    3
#b    2
#d    1
#Name: count, dtype: int64

它会默认给你按降序排列,若不想,传入属性 sort=False

.isin([]) 可以执行成员属性检查,可将

obj
#0    c
#1    a
#2    d
#3    a
#4    a
#5    b
#6    b
#7    c
#8    c
#dtype: object

mask = obj.isin(["b", "c"])

mask
#0     True
#1    False
#2    False
#3    False
#4    False
#5     True
#6     True
#7     True
#8     True
#dtype: bool

obj[mask]
#0    c
#5    b
#6    b
#7    c
#8    c
#dtype: object

其实就是布尔索引

DataFrame的 .value_counts() 有点不一样,你不选值光只有这个方法,它会将每行当作元素,计算不同行的计数:

data = pd.DataFrame({"a": [1, 1, 1, 2, 2], "b": [0, 0, 1, 0, 0]})

data
   a  b
0  1  0
1  1  0
2  1  1
3  2  0
4  2  0

data.value_counts()
a  b
1  0    2
2  0    2
1  1    1
dtype: int64

OK了,这第5章可算结束了,我效率太低了有点儿,我要改变这个笔记的写法和分配给写笔记的时间分配了,太浪费时间了,下一章开始我要缩短笔记了

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值