pandas
简介
-
基于numpy的工具
-
高效提供了大量库和一些标准的数据模型
相当于Numpy的一维数组,但是与普通的一维数组不同,Series支持对数据的自定义标签也就是index(索引),通过索引可以访问Series的元素
Series
索引
简单的创建
import pandas as pd
sel = pd.series()
print(sel)
我们发现当我们没有创建索引的时候回自动创建一个类似于下标的索引,从0开始,
索引的设置
使用data设置值,index设置索引
import pandas as pd
sel = pd.Series(data=[5,4,3,2],index=[1,2,3,4])
print(sel)
通过数组对象的index属性可以直接获取索引
value 可以获取值
import pandas as pd
sel = pd.Series(data=[1,2,3,4,5],index=list('abcde'))
print(sel.values) # 获取值
print(sel.index) # 获取索引
print(list(sel.iteritems())) # 获取索引与值的键值对列表
[1 2 3 4 5]
Index([‘a’, ‘b’, ‘c’, ‘d’, ‘e’], dtype=‘object’)
[(‘a’, 1), (‘b’, 2), (‘c’, 3), (‘d’, 4), (‘e’, 5)]
字典转Series
import pandas as pd
pd_dict = {"red":20,"blue":40,"green":40}
sel = pd.Series(pd_dict)
print(sel)
red 20
blue 40
green 40
dtype: int64
通过 索引|位置 得到值
通过 数组[索引] 可以获取值
import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
print(sel["red"])
20
位置也可以获取值,位置实际就是从0开始的数组下标索引
通过索引|位置列表组可以获取数组的多个值
import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
print(sel[['red','blue']])
red 20
blue 40
dtype: int64
修改index值
- 使用index修改数组本身索引
import pandas as pd
sel = pd,Series({"red":20,"blue":40,"green":40})
sel.index=['r','g','b']
- 使用reindex修改数组索引获取新数组
import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
ret = sel.reindex(['r','g','b'])
如果index超出了值的长度,那么默认补全NaN给超出的部分
drop 删除值
使用drop删除指定索引的数据,传入列表就是删除多个
import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
ret = sel.drop(["red","green"]) # 删除索引red与green
一维数组的计算
Series支持numpy的计算,且Series的计算与index进行对应即映射,相同的index进行运算,如果运算的一方无法与另一方对应那么运算的结果该index的值就是NaN
import pandas as pd
selA = pd.Series({"banana":12,"apple":41,"orange":100})
selB = pd.Series({"banana":42,"apple":11,"gruy":100})
selC = selB + selA
print(selC)
apple 52.0
banana 54.0
gruy NaN
orange NaN
dtype: float64
DataFrame
DataFrame是一个pandas的二维数组 所以他有两个索引一个是行索引一个是列索引
基础使用
创建DataFrame(DF)
import pandas as pd
import numpy as np
DF_A = pd.DataFrame(np.random.randint(0,10,(4,4)))
# 将四行四列的随机numpy数组转化成DF
0 1 2 3
0 8 2 2 3
1 9 4 8 4
2 4 4 2 5
3 3 9 5 8
修改两个索引的方法
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(0,100,(4,4)))
# 也可以使用index参数与columns参数修改
df.index = [1,2,3,4] # 行索引
df.columns = list("ABCD") # 列索引
print(df)
字典转DF
通过字典生成的DF,字典的键会自动成为列索引,其值会成为该列的数组元素
import pandas as pd
dict_pd = {"banana":[10,12,9],"orange":[99,121,160]}
df = pd.DataFrame(dict_pd,index=["超市A","超市B","超市C"])
print(df)
banana orange 超市A 10 99 超市B 12 121 超市C 9 160
当然我们可以值为Series的字典来创建数组
常用属性方法
import pandas as pd
data = pd.DataFrame(
{"banana": [10, 12, 9], "orange": [99, 121, 160]},
index=["超市A", "超市B", "超市C"]
)
index = data.index.tolist() # 将DF的index收集成列表
columns = data.columns.tolist() # 将DF的列索引收集成列表
shape = data.shape # 获取数组而定行与列
print(data.shape) # DF 的维度
print(shape)
print(data.values) # DF数据查看
print(data.info) # 数据的信息
print(data.head(2)) # 查看前2行,默认是前五行
print(data.tail()) # 显示后几行
- rename()
- 可以修改DF的 index 与 colunms的值
def rename_index(x):
return str(x + 1)
def rename_columns(x):
return x.replace("User", "")
a = df.rename(index=rename_index, columns=rename_columns)
print(a)
A B
1 1 2
2 2 3
3 4 5
4 5 6
5 6 1
使用键值对来修改单一的index 与 columns 也是可以的
- set_index
- 将指定列的值赋值给index
b = df.set_index("UserA",drop=False)
print(b)
定位
获取指定列
import pandas as pd
data = pd.DataFrame(
{"banana": [10, 12, 9], "orange": [99, 121, 160]},
index=["超市A", "超市B", "超市C"]
)
col_banana = data["banana"]
# 获取指定的列 返回值DF
print(data[["banana","orange"]])
# 获取多列返回值DF
print(df.loc[:,["UserA"]]) # 获取UserA列
ret = data[0:1]
# 获取一行
指定行
使用loc定位行
import pandas as pd
df = pd.DataFrame({"UserA":[1,2,4,5,6],"UserB":[2,3,5,6,1]})
data_userA_col = df["UserA"].loc[1:4] # 获取UserA列的 1—4index 的值
print(data_userA_col)
添加列与行
运算方法
import pandas as pd
df = pd.DataFrame({"UserA": [1, 2, 4, 5, 6], "UserB": [2, 3, 5, 6, 1]})# 以后数据源就是这个了
# 追加
df["UserC"] = [4,2,3,4,5] # 长度需要与row相同
# 指定插入
list_col = df.columns.tolist() # 将列转为列表
list_col.insert(2,"UserD") # 向列表插入数据
e = df.reindex(columns=list_col) # 修改数组的列 reindex 可以对原行列索引重新构建索引值
e["UserD"] = [4,1,5,4,1]
print(e)
# 指定插入II
f = df
f.insert(2,"userD",[24,5,1,2,4])
print(f)
append
- 将两个DF合并成一个DF
dataA = pd.DataFrame({"name":["刘博源"],"Tel":[1271124111]})
dataB = pd.DataFrame({"name":["李三"],"Tel":[1212341111]})
data_sum = dataB.append(dataA,ignore_index=True)
print(data_sum)
# ignore_index:是否忽略index,如果是True值,那么添加的时候就忽略添加数据的index值,而是递增之前的index如果是ignore_index=False 那么就会保留之前的index(视频有个错误,,)f
行列合并
ret = pd.concat([df1,df2],axis=1) # 按行拼接 X + X
'''
{——}{——}
'''
ret2 = pd.concat([df1,df2],axis=0) # 安列拼接
'''
{--}
{--}
'''
数据处理
滤除缺失值
- dropna()
import pandas as pd
import numpy as np
#Series
df = pd.Series([np.nan,12,np.nan])
print(df)
df_noNan = df.dropna()
df_notNan = df.notnull() # 广播是否为Nan
#Pandas
df1 = pd.DataFrame([[1,2,4],[4,1,np.nan],[np.nan,13,1]])
df_clear = df1.dropna()
‘’‘
dropna params:
axis
默认 : 过滤行
axis = 1:过滤列
how
判断方式 为 any 时 就是遇见Nan就删除整行|列
判断方式 为 all 时 就是全部Nan就删除整行|列
thresh
保留 至少hresh个非nan的 行|列
’‘’
print(df_clear) # 过滤所有包含Nan值的行
填充空值
- fillna()
df1.fillna(1) # 将所有为Nan的Value替换成1
'''
inplace:是否在原数组修改不返回值,默认False
'''
df1 = pd.DataFrame([[1,2,4],[4,1,np.nan],[np.nan,13,1]],columns=[1,2,3],index=list("abc"))
df1.fillna({1:3,2:4,3:4})
# 根据columns进行字典的填充
# 指定列(位置)填充
df1.fillna([:,1].fillna(5,inplace=True))
# 前后值填充
ret = df.fillna(method="bfill") # 使用后面(下一行同列)的值填充
ret = df.fillna(method="ffill") # 使用前面(上一行同列)的值填充
'''
params:
limit:
限制的填充的行数
axis:
改变填充的方向
1:
同行不同列的填充
0:
同列不同行的填充
'''
去重
df = pd.DataFrame({"A":[1,2,1,5,6,1],"B":[1,2,1,5,1,2]})
print(df)
isRepeat = df.duplicated() # 判断是否重复(行与行之间的)
print(isRepeat)
dropedRepeat = df.drop_duplicates() # 删除重复行
print(dropedRepeat)
# 可以传入一个列表参数给drop_duplicates代表指定的列的重复判断
'''
params:
inplace:修改原数组不返回新数组
'''
数据合并
join()
df3 = pd.DataFrame({"Red": [1, 4, 5], "Blue": [2, 4, 5], "Yellow": [1, 5, 6]},index=list("123"))
df4 = pd.DataFrame({"Gury": [1, 4, 5], "Green": [2, 4, 5], "Purple": [1, 5, 6]},index=list("124"))
print(df3.join(df4))
'''
将 df3 与 df4 进行横向的合并
index会默认以df3 join的调用者为基础
参数df4多余的idnex在合并时被忽略
没有的index合并值为填充为NaN
params:
how:如何合并
-left 左合并
默认的合并法则
- right 右合并
index会以 df4 参数为基础进行合并
- outer 全合并
index 会将进行合并的两个数组全部的索引为基础进行合并
'''
merge()
pd_char = pd.DataFrame(data=[[1001,"向老表","A"],[1002,"刘天皇","B"],[1003,"佘东阳","C"],[1004,"岳顺江","C"]],columns=["编号","名称","工程编号"])
pd_project = pd.DataFrame(data=[["A","数据处理集群管理"],["B","全栈开发"],["C","前端开发&数据可视化"]],columns=["工程编号","名称"])
pd_sum = pd.merge(pd_char,pd_project,on="工程编号",suffixes=['_人物','_工程'],how="inner") # on 相当于链接的键值
'''
how:
inner :内连接
left:左连接
right:有链接
outer:外连接
suffixes:
如果链接的两个DF有相同的Columns那么我们就可以给这两个DF的同名索引取后缀,suffixes可以规定两个后缀
'''
print(pd_sum)
编号 名称_人物 工程编号 名称_工程 0 1001 向老表 A 数据处理集群管理 1 1002 刘天皇 B 全栈开发 2 1003 佘东阳 C 前端开发&数据可视化 3 1004 岳顺江 C 前端开发&数据可视化
多层索引
我们可能会遇见这种索引
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iCorbOOS-1618501305960)(Pandas.assets/image-20210414023130871.png)]
也就是一个大索引可以分为多个小索引(层级索引),多个小索引又有多个具体的值
如何具体去创建这样的数组 我们可以通过以下的代码
Series多层索引数组
import numpy as np
import pandas as pd
Mark_Numpy = np.random.randint(40,100,size=(6,))
Mark_Sum_version_1 = pd.Series(Mark_Numpy,index=['a','a','b','b','c','c']) # 简单的创建
Mark_Sum_version_2 = pd.Series(Mark_Numpy,index=[['a','a','b','b','c','c'],["语文","数学","语文","数学","语文","数学"]]) # 多层索引 创建
print(Mark_Sum_version_2)
print(Mark_Sum_version_2["a","数学"])# 值的定位,注意区分取多值与这个的差别
'''
补充
使用iloc定位的时候因为使用的是位置
索引传入的小标还是最内层的索引
'''
a 语文 60
数学 64
b 语文 49
数学 94
c 语文 79
数学 51
dtype: int32
DataFrame多层索引数组
这里讨论的多索引是index方面的而不是columns方面的
# 方法一
multilayerDF = pd.DataFrame(
data=numpy_array,
columns=["入学成绩","月考成绩","期末成绩"],
index=[
['刘博源','刘博源','刘博源',"向国骑","向国骑","向国骑"],
["java",'python','English',"java",'python','English']
]
)
print(multilayerDF)
# 方法二
outterIndex = ["刘博源", "刘博源", "刘博源", "向国骑", "向国骑", "向国骑"] # 外层索引
innerIndex = ["java", 'python', 'English', "java", 'python', 'English'] # 内层索引
mi = pd.MultiIndex.from_arrays([outterIndex, innerIndex]) # 创建索引
df2 = pd.DataFrame(data=np.random.randint(10, 40, (6, 3)), index=[outterIndex, innerIndex])
print(df2)
# 方法三 可以不用格式数组里面究竟有多少个重复的元素
outterIndex = ["刘博源",'向国骑']
innerIndex = ["Python","Java","English"]
mi = pd.MultiIndex.from_product([outterIndex,innerIndex]) # 创建索引
Mpd = pd.DataFrame(np.random.randint(1,100,(6,3)),index=mi)
print(Mpd)
# 利用这种方式创建一个Series试试?
入学成绩 月考成绩 期末成绩 刘博源 java 67 82 82 python 92 67 97 English 87 72 85 向国骑 java 81 75 68 python 80 53 94 English 84 65 75
多索引数组的取值
# S是一个多层索引Series D是一个多层索引DataFrame
print(S['a']) # 外层索引为a
print(S[['a','b']]) # 外层索引为a,b(多外层获取)
print(S["a","期末"]) # 取a索引的期末子索引
print(D.loc["a"])# 外层索引为a
print(D[['a','b']])# 外层索引为a,b(多外层获取)
print(D["a","期末"])# 取a索引的期末子索引
时间序列
时间序列可以用来处理表示时间的数据
使用date_range()生成有序的时序
单位:
D 日历上的每一天
B 工作日的每天
H 每小时
T|min 每分钟
S 每秒
L|ms 每毫秒
M 日历日的月底日期
BM 工作日的月底日期
MS 日历日的月初日期
BMS 工作日的月初日期
freq:
日期偏移量
时间的间隔是一个字符串,格式为:数字+单位
比如:10D 代表间隔十天
periods:
固定时期
取值为整数或者None
start, end, periods, freq 不能同时存在
# 创建含有时序的DF
data = pd.date_range(start='20190101', end='20200101') # 创建时序
time_dataFrame = pd.DataFrame(
data=np.random.randint(80,700,(data.size,3)),
index=data, # 通过时序创建index索引
columns=["早上","中午","晚上"]
)
# 时序df的检索
year_2019_date = time_dataFrame['2019'] # 获取2019年的数据
year_2019_month_5_date = time_dataFrame['2019-05'] # 获取2019年5月份的数据
year_2019_during_5mTo8m = time_dataFrame.loc['2019-05-01':'2019-08-01'] # 获取五月到8月的数据
'''
这里可以不使用loc:
year_2019_during_5mTo8m = time_dataFrame['2019-05-01':'2019-08-01'] # 获取五月
但是这样会报警告
'''
print(year_2019_during_5mTo8m)
date = pd.date_range(start="2021-04-15",periods=40,freq='2h')
df = pd.DataFrame(np.random.randint(1,40,(date.size,3)),index=date)
during_1pTo5p = df.between_time("1:00","5:00")
print(during_1pTo5p)
# 获取每天在1点到五点的数据
分组聚合
分组
df = pd.DataFrame(columns=["姓名", "订单编号", "金额"],
data=[["刘博源", 1, 1020], ["李四", 2, 1000], ["李四", 3, 1050], ["刘博源", 4, 1920], ["向国骑", 5, 1100]])
groupbyName_df = df.groupby("姓名") # 通过姓名分组
AllGroupsbyName = groupbyName_df.groups # 通过分组操作分出来的组(键值对出现 键是分组依据 值是对应的索引序列)
print(AllGroupsbyName)
count_group_name_df = groupbyName_df.count() # 查看分组的分组数与其对应值的数量
for name,Group in groupbyName_df: # 使用遍历可以对分组结果进行对象解析
print(name) # 依次分组的数组名称
print(Group) # 以关键字分组的数组小组
Path_df_LiSi = groupbyName_df.get_group("李四") # 获取按名称分组的李四组
groupNameOnlySeeOrderColumns = df["订单编号"].groupby(df["姓名"])
# 如果只看DF的某列对全DF分组
print(groupNameOnlySeeOrderColumns.get_group("向国骑"))
groupNameAndJinE_df = df.groupby(["姓名",'金额'])
聚合
# 可以对分组后的对象进行聚合函数运算
# 题:通过下列数组求出每个人的总和
df_mark = pd.DataFrame([["刘博源","英语",100],["向国骑","英语",90],["刘博源","python",82],["向国骑","Java",75]],columns=["考生","科目","成绩"])
df_mark_groupByName = df_mark.groupby("考生")
df_mark_avg = df_mark_groupByName.sum()
# 在进行运算的时候不是数值的列 会被清除 但是会保留分组依据列索引
print(df_mark_avg)
df_mark2 = pd.DataFrame([[1001,"刘博源", "英语", random.randint(1, 100)], [2001,"向国骑", "英语", random.randint(1, 100)],
[1002,"刘博源", "python", random.randint(1, 100)], [2002,"向国骑", "Java", random.randint(1, 100)]],
columns=["考号","考生", "科目", "成绩"])
df_onlyOperaMark = df_mark2["成绩"].groupby(df_mark2["考生"]) # 只计算成绩列 不去计算考号列
print(df_onlyOperaMark.sum())
df_onlyOperaMark = df_mark2.groupby("考生")["成绩"].sum() # 另一种写法
print(df_onlyOperaMark)
其他的聚合函数
聚合函数 | 作用 |
---|---|
min() | 最小值 |
max() | 最大值 |
sum() | 求和 |
mean() | 平均数 |
std() | 标准差 |
size() | 按照groupby的值计算该值的个数 与count函数的区别在于,size函数会计算NAN值,而count函数不会计算NAN值 |
nunique() | 去掉重复值后进行计数 |
count() | 计算个数 |
agg自定义聚合函数
使用agg可以使用聚合函数
df_grouper.agg("sum") # df.sum()
df_grouper.agg("count") # df.agg()
df_grouper.agg("sum","count") # 多聚合
def peak_range(df):
return df.max() - df.agg('min')
df_grouper.agg(peak_range)
apply
用处一:可以通过自定义函数来筛选数据
def isSixStar(value):
return True if value == 6 else False
df_data = pd.DataFrame(
[["能天使", "狙击", 6, 241], ["克洛斯", "狙击", 3, 41], ["莫斯提马", "术士", 6, 511], ["月见夜", "近卫", 3, 51], ["安洁莉娜", "辅助", 6, 102]],
columns=["名称", "类型", "星级", "攻击"])
# df_group_job = df_data.groupby("类型")
is_six_star = df_data["星级"].apply(isSixStar) # 判断是否为6星人物
print(is_six_star)
df_data["是否为六星"] = is_six_star
print(df_data)
def sort_gat_fristAndSecond(df, columns, wight):
'''
通过列排序_从小到大
:param df: 参与排序的数组
:param columns:指定列
:param wight: 前几位
:return: 数组更具排序前几位列
'''
sort_df = df.sort_values(by=columns)
print(sort_df)
df_temp.groupby("星级").apply(sort_gat_fristAndSecond, columns="攻击", wight=2)