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章可算结束了,我效率太低了有点儿,我要改变这个笔记的写法和分配给写笔记的时间分配了,太浪费时间了,下一章开始我要缩短笔记了