Pandas是一个强大的分析和操作大型结构化数据集所需的工具集
基础是Numpy,提供了高性能矩阵的运算
提供了大量能够快速便捷地处理处理数据的函数和方法
应用于数据挖掘,数据分析,提供数据清洗功能
一、Pandas的数据结构
导入pandas:
import pandas as pd
from pandas import Series, DataFrame
import numpy as np
import matplotlib.pyplot as plt
1、Series
series是一种类似于一维数组的对象,由一组数据和与之对应的索引组成。
索引(index)在左,数据(values)在右,索引是自动创建的
(1)series的创建
特别地,由ndarray创建的是引用,而不是副本,对Series元素的改变也会改变原来的 ndarray对象中的元素。
# 1、通过list或者numpy数组创建
# 默认索引为0-N-1的整数型索引
s = Series(np)
# 可以通过设置index参数指定索引
s.index = list("abcde")
s2 = Series(range(10, 30))
# 2、通过dict构建
s3 = Series({1000: "hello", 2000: "world", 3000: "!"})
(2)Series的索引和切片
可以使用中括号取单个索引(此时返回的是元素类型),或者中括号里一个列表取多个索引(此时返回的仍是Series类型)。
# 显式索引:使用index中的元素作为索引值 / 使用.loc[]
# 注意此时是闭区间
s = Series(np.random.random(7), index = list("abcdefg"))
print(s)
s.loc["a"]
# 隐式索引:使用整数作为索引值 / 使用.iloc[]
# 注意此时是半开区间
s.iloc[0]
# 进行切片
s.loc["a": "c"]
(3)Series的基本概念
可以把series看成一个定长的有序字典
可以通过shape,size,index,values等得到series的属性
display(s.shape, s.size, s.index, s.values)
# 可以通过head(), tail()快速查看Series对象的样式
s.head(3) # 打印前三行
s.tail(3) # 打印后三行
# 当索引没有对应的值时,可能出现去缺失数据显示NaN的情况
s = Series([1, 26, None, np.nan], index=list("风火雷电"))
# 可以使用pd.isnull(), pd.notnull()来检测缺失数据
s1 = s.notnull()
# 获取不为空的数据
s[s1]
# Series对象本身及其实力都有一个name属性
# 对于dataFrame来说,name就是列名
(4)Series的运算
在运算中自动对齐不同索引的数据
如果索引不对应,则补NaN
s1 = Series([2, 4, 7 ,9], index=[0, 1, 2, 3])
s2 = Series([1, 2, 3, 4], index=[2, 3, 4, 5])
# 在进行相加时,索引相同的进行相加
s1+s2
# 如果想要保留所有的index,需要使用.add()函数
s1.add(s2, fill_value=0)
2、DataFrame
是一个表格型的数据结构,可以看做是由series组成的字典,由按一定顺序排列的多列数据组成。既有行索引(自动创建),也有列索引。
行索引:index 列索引:columns 值:values
最常用的方法是传递一个字典来创建。以字典的键作为每一列的名称,以字典的值作为每一列。 此外,DataFrame会自动加上每一行的索引(和Series一样, 若传入的列与字典的键不匹配,相应的值为NaN。
(1)创建DataFrame
# 通过ndarray构建DataFrame
arr_obj = np.random.rand(3, 4)
df_obj = DataFrame(arr_obj)
# 通过dict构建DataFrame
df = DataFrame(
{
"height": [175, 180, 169, 188],
"age": np.random.randint(18, 25, size=4),
"sex": ["男", "女", "男", "女"],
"weight": [60, 50, 77, 80]
},
index=list("ABCD"),
columns=["height", "age", "sex", "weight"]
)
(2)DataFrame的索引
# 1、对列进行索引
# - 通过类似字典的方式
# 检索列的返回值时Series
age = df["age"]
display(type(age))
# - 通过属性的方式
# 对于DataFrame而言,列就相当于属性
df.age
# 2、对行进行索引
# - 使用.loc[]加 index来进行行索引
# 对于行的检索,返回值也是Series
df.loc["A"]
# 如果检索多行返回的数据就是DataFrame
df.loc[["A", "B"]]
# 切片是行切片
df.loc["A": "C"]
# - 使用.iloc[]加整数来进行行索引,左闭右开
df.iloc[1: 3]
# 检索行的时候,参数可以是多个,但是列不行
df.loc[["sex", "B"]]
(3)DataFrame的运算
同Series一样:
在运算中自动对齐不同索引的数据
如果索引不对应,则补NaN
# 创建月考一不同人员的各科目成绩
df1 = DataFrame(np.random.randint(0, 150, size=(4, 4)),
index=["张三", "李四", "王五", "赵六"],
columns=["语文", "数学", "英语", "Python"])
# 月考二,有新学生转入
df2 = DataFrame(np.random.randint(0, 150, size=(5, 3)),
index=["张三", "李四", "王五", "赵六", "田七"],
columns=["语文", "数学", "英语"])
df1 + df2
# 加和的结果
df1.add(df2, fill_value=0)
(4)Series与DataFrame之间的运算
【重要】
使用python操作符:以行为单位操作(参数必须是行),对所有行都有效。类似于Numpy中二维数组于一维数组的运算,但可能出现NaN。
使用pandas操作函数,如add():
axis=0:以列为单位操作(参数必须是列),对所有列是有效。
axis=1:以行为单位操作(参数必须是行),对所有行都有效。
s1 = df1["Python"]
df1 + s1 # 索引不同,所得的数据均为NaN
ss = df1["Python"] # 此时是列索引,对所有行操作,所以参数为行
df1.add(ss, axis=0)
ss2 = df1.loc["张三"] # 此时是行索引,对所有列操作
df1.add(ss2, axis=1)
二、Pandas层级索引
1、隐式构造
最常见的方法是给DataFrame构造函数的index参数传递两个或更多的数组
series也可以创建多层索引
# Series
s = Series([1, 2, 3, 4], index=[["a", "a", "b", "b"], [0, 0, 1, 1]])
# DataFrame
df = DataFrame(np.random.randint(0, 150, size=(6, 3)),
columns=["语文", "数学", "Python"],
index=[["Michael", "Michael", "Lisa", "Lisa", "PO", "PO"], ["middle", "end", "middle", "end", "middle", "end"]])
2、显式构造
(1)使用数组
df1 = DataFrame(np.random.randint(0, 150, size=(6, 3)),
columns=["Java", "Html5", "Python"],
index=pd.MultiIndex.from_arrays([["张三", "张三", "李四", "李四", "王五", "王五"], ["期中", "期末", "期中", "期末", "期中", "期末"]])
)
df1
(2)使用元组
df2 = DataFrame(np.random.randint(0, 150, size=(6, 3)),
columns=["Java", "Html5", "Python"],
index=pd.MultiIndex.from_tuples([("张三", "期中"), ("张三", "期末"),("李四", "期中"), ("李四", "期末"), ("王五", "期中"), ("王五", "期末")])
)
df2
(3)使用product,最简单
df3 = DataFrame(np.random.randint(0, 150, size=(6, 3)),
columns=["Java", "Html5", "Python"],
index=pd.MultiIndex.from_product([["张三", "李四", "王五"], ["期中", "期末"]])
)
df3
(4)DataFrame列同样可以设置多层索引
df4 = DataFrame(
np.random.randint(0, 150, size=(3, 6)),
columns = pd.MultiIndex.from_product([["Java", "Html", "Python"], ["期中", "期末"]]),
index = ["张三", "李四", "王五"]
)
df4
三、多索引对象的索引与切片操作
1、Series的操作
【重要】对于Series来说,直接中括号[]和.loc完全一样,因此,推荐使用中括号索引和切片
# 索引
s = Series([1, 2, 3, 4], index=[["a", "a", "b", "b"], [0, 1, 0, 1]])
s
s["a", 0] # 第一个参数,多层索引的第一维,第二个参数是第二维
s[["a", "期中"]] # 其实此时只有一个参数
# 切片
s["a": "b"]
s.iloc[0: 3]
2、DataFrame的操作
可以直接使用列名称来进行列索引
使用行索引需要使用loc()函数
【注意】在对行索引的时候,若一级索引还有多个,对二级索引会遇到问题,也就是 无法直接对二级索引进行索引,必须等到让二级索引变成一级索引后才能对其进行索引
df1["张三" : "李四"]
# df1["张三", "期中"] # 报错
df1.loc["张三", "期中"]
# df1.loc["张三"]["期中"] 报错
df1.loc["张三"].loc["期中"]
四、索引的堆
【技巧】使用stack()的时候,level等于哪一个,哪一个就消失,出现在行里
使用unstack()的时候,level等于哪一个,那一个就消失,出现在列里
df4.stack(level=0)
df2.unstack(level=1)
五、Pandas的拼接操作
级联:pd.concat,pd.append
合并:pd.merge, pd.join
# 生成2个3*3的矩阵,对其分别进行两个维度上的级联
nd1 = np.random.randint(0, 10, size=(3, 3))
nd2 = np.random.randint(0, 10, size=(3, 3))
np.concatenate((nd1, nd2), axis=0) # 0是第一维的方向:行
1、简单级联
和np.concatenate一样,优先增加行数(默认axis=0)
df1 = DataFrame(np.random.randint(1, 10,size=(2, 2)),
index=[0, 1],
columns=["A", "B"])
df2 = DataFrame(np.random.randint(1, 10, size=(2, 2)),
index=[2, 3],
columns=["A", "B"])
pd.concat([df1, df2])
# 注意index在级联时可以重复
df3 = DataFrame(np.random.randint(1, 10, size=(3, 2)),
index=[0, 1, 2],
columns=["A", "B"])
df4 = pd.concat([df1, df3])
df4
# 也可以选择忽略ignore_index,重新索引
pd.concat([df1, df3], ignore_index=True)
# 或者使用多层索引keys
pd.concat([df1, df3], keys=["x", "y"])
2、不匹配级联
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致。
(1)外连接:补NaN(默认方式)
df1 = DataFrame(np.random.randint(1, 10, size=(2, 2)),
index=[1, 3],
columns=["A", "B"])
df2 = DataFrame(np.random.randint(1, 10, size=(2, 2)),
index=[2, 4],
columns=["B", "C"])
pd.concat([df1, df2], join="outer")
(2)内连接:只连接匹配的项
pd.concat([df1, df2], join="inner")
# 连接指定轴join_axes,以某一个DataFrame的列索引为新的列索引值
df3 = DataFrame(np.random.randint(1, 10, size=(3, 3)),
index=[0, 1, 2],
columns=["A", "C", "D"])
df4 = DataFrame(np.random.randint(1, 10, size=(3, 3)),
index=[3, 4, 5],
columns=["C", "D", "F"])
pd.concat([df3, df4], join_axes=[df3.columns])
(3)使用apppend()函数添加
由于在后面级联的使用非常普遍,因此有一个函数append专门用与在后面添加
df1 = DataFrame(np.random.randint(1, 10, size=(5, 2)),
index=[0, 1, 2, 3, 4],
columns=["大众", "福克斯"])
df2 = DataFrame(np.random.randint(1, 10, size=(5, 2)),
index=[5, 6, 7, 8, 9],
columns=["大众", "福克斯"])
df1.append(df2)
3、merge合并
merge与concat的区别在于,merge需要依赖某一共同的行或列来进行合并。
使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来合并。
(1)一对一合并
df1 = DataFrame({"employee": ["Po", "Sara", "Danis"],
"group": ["sail", "counting", "marketing"]})
df2 = DataFrame({"employee": ["Po", "Sara", "Dansi"],
"work_time": [2, 3, 1]})
display(df1, df2)
pd.merge(df1, df2)
(2)多对一合并
df1 = DataFrame({"employee": ["Po", "Sara", "Danis"],
"group": ["sail", "counting", "marketing"]})
df2 = DataFrame({"employee": ["Po", "Po", "Dansi"],
"work_time": [2, 3, 1]})
display(df1, df2)
pd.merge(df1, df2)
(3)多对多合并
df1 = DataFrame({"employee": ["Po", "Po", "Danis"],
"group": ["sail", "counting", "marketing"]})
df2 = DataFrame({"employee": ["Po", "Po", "Dansi"],
"work_time": [2, 3, 1]})
display(df1, df2)
pd.merge(df1, df2) # 在进行多对多合并时,每一个数据都没有放过
4、key的规范化
使用on=显式指定哪一列为key,当有多个key相同时使用
df3 = DataFrame({"employer": ["Po", "Summer", "Flower"],
"group": ["sail", "marketing", "serch"],
"salary": [12000, 10000, 8000]})
df4 = DataFrame({"employee": ["Po", "winter", "Flower"],
"group": ["sail", "marketing", "serch"],
"work_time": [2, 1, 5]})
display(df3, df4)
pd.merge(df3, df4, on='employee')
pd.merge(df3, df4, on='group', suffixes=["_a", "_b"])
使用left_on和right_on指定左右两边的列作为key,当左右两边的key都不相等时使用
pd.merge(df3, df4, left_on="employer", right_on="employee")
5、内合并与外合并
内合并:只保留两者都有的key(默认模式)
外合并:全部保留
df1 = DataFrame({"age": [18, 22, 33], "height": [175, 169, 180]})
df2 = DataFrame({"age": [18, 23, 31], "weight": [65, 70, 80]})
display(df1, df2)
pd.merge(df1, df2, how="outer")
pd.merge(df1, df2, how="inner")
pd.merge(df1, df2, how="left") # 左合并
pd.merge(df1, df2, how="right") # 右合并
六、Pandas数据处理
1、删除重复元素
使用duplicated()函数检测重复的行,返回元素为布尔类型的Series对象,每个元素对应一行,如果该行不是第一次出现则元素为True
df = DataFrame(np.random.randint(0, 3, size=(6, 2)),
index=["张三", "李四", "王五", "李四", "小吴", "张三"],
columns=["语文", "数学"])
# 检查重复元素
df.duplicated()
# 使用duplicates()函数删除重复的行 ,列名不能重复,否则操作会报错
df.drop_duplicates()
# 如果使用pd.concat([df1, df2], axis=1) 生成新的DataFrame,新的df中columns相同,使用duplicate和drop_duplicates都会出现问题
2、数据的映射
创建一个映射关系列表,把values元素和一个特定的标签或者字符串绑定
df = DataFrame(np.random.randint(10, 100,size=(3, 3)),
index=["张三", "李四", "王五"],
columns=["Python", "Java", "UI"])
df["Python"] = df["Python"].map(lambda x: x+10)
# map()不仅可以根据条件修改当前列,还可以映射新一列数据,我们可以在map()
# 中使用lambda表达式,还可以使用方法,可以自己实现的方法
df["Html"] = df["Java"].map(lambda x: x+3)
3、reanme()函数:替换索引
仍然是新建一个字典
index = {"张三": "zhangSir", "李四": "liSir"}
df.rename(index=index)
4、异常值检测和过滤
使用describe()函数查看每一列的描述性统计量
df = DataFrame(np.random.randint(0, 150, size=(6, 3)),
index=list("ABCDEF"),
columns=list("语数外"))
df.describe() # std()标准方差
5、使用.take()函数排序
可以借助np.random.permutation()函数随机排序
per = np.random.permutation(6)
df.take(per)
6、数据聚合
数据聚合是数据处理的最后一步,通常是要使每一个数组生成一个单一的数值。
数据分类处理:
分组:先把数据分为几组
用函数处理:为不同组的数据应用不同的函数以转换数据
合并:把不同组得到的结果合并起来
数据分类处理的核心:groupby()函数
df = DataFrame({"item": ["苹果", "香蕉", "橘子", "香蕉", "橘子", "苹果"],
"price": [4, 3, 3, 2, 4, 2],
"color": ["red", "yellow", "yellow", "green", "red", "green"]})
g = df.groupby("item")
g.groups
g["price"].mean() # 求平均值
df = DataFrame({
"item": ["萝卜", "白菜", "辣椒", "冬瓜", "白菜", "辣椒", "冬瓜", "白菜"],
"color": ["red", "red", "green", "green", "white", "green", "white", "white"],
"weight": np.random.randint(50, 150, size=8),
"price": np.random.randint(1, 4, size=8)
})
# 对df进行聚合操作,求出颜色为白色的价格总和
g = df.groupby("color")["price"]
g.sum()
g.sum()["white"]