DataFrame
DataFrame是一个【表格型】的数据结构,可以看做是【由Series组成的字典】(共用同一个索引)。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
行索引:index
列索引:columns
值:values(numpy的二维数组)
1、DataFrame的创建
最常用的方法是传递一个字典来创建。DataFrame以字典的键作为每一【列】的名称,以字典的值(一个数组)作为每一列。
此外,DataFrame会自动加上每一行的索引(和Series一样)。
同Series一样,若传入的列与字典的键不匹配,则相应的值为NaN。
# 字典创建
df1 = DataFrame({"Python":[99,98,89,97,88],
"Java":[56,75,46,86,39],
"C++":[98,54,76,45,84]},
index = list("abcde"),
columns = ["Python","Java","C++","PHP"])
# index(列索引不能多也不能少)
# columns(行索引可多可少:多的为NaN,少的不显示)
DataFrame属性:values、columns、index、shape
df1.values -- 打印value值
df1.columns -- 打印列索引
df1.shape -- 打印形状
df1.index -- 打印行索引
# ndarray对象创建
df2 = DataFrame(data=np.random.randint(0,100,size=(5,4)),
index = list("abcde"),
columns = ["Python","Java","Html","PHP"])
2、DataFrame的索引
1、 对列进行索引
1. 通过类似字典的方式
2. 通过属性的方式
可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引,且name属性也已经设置好了,就是相应的列名。
df2.Python
df2["Python"] -- 这个方法比较常用(列表中可以传多个值,用逗号隔开)
# 列不支持切片操作
2、 对行进行索引
1. 使用.loc[]加index来进行行索引
2. 使用.iloc[]加整数来进行行索引
同样返回一个Series,index为原来的columns。
# 行索引不支持直接使用中括号(即df2[]是错误的写法)
df2.loc["a"] 或 df2.iloc[0] -- 这两种方法(使用一个中括号)得到的是Series对象
df2.loc[["a"]] 或 df2.iloc[[0]] -- 使用两个中括号得到的是DataFrame对象
-- 可以传多个值,用逗号隔开
df2.loc["a":"c"] -- 闭区间(左闭右闭)
df2.iloc[0:3] -- 左开右闭区间
3、 对元素索引的方法
1. 使用列索引
2. 使用行索引(iloc[3,1]相当于两个参数;iloc[[3,3]] 里面的[3,3]看做一个参数)
3. 使用values属性(二维numpy数组)
df2["Python"]["c"] -- 先列索引,后行索引
df2.loc["c"]["Python"] -- 先行索引,后列索引
# 【注意】 直接用中括号时:
索引表示的是列索引
切片表示的是行切片
3、DataFrame的运算
1、 DataFrame之间的运算
同Series一样:
在运算中自动对齐不同索引的数据
如果索引不对应,则补NaN
df1+df2 df1.add(df2, fill_value=0)
Html Java Python Ruby 数学 英语 语文 Html Java Python Ruby 数学 英语 语文
a NaN NaN 131 NaN NaN NaN NaN a 121.0 132.0 131 125.0 120.0 NaN 123.0
b NaN NaN 235 NaN NaN NaN NaN b 1.0 123.0 235 48.0 136.0 NaN 114.0
c NaN NaN 228 NaN NaN NaN NaN c 130.0 77.0 228 29.0 141.0 NaN 130.0
d NaN NaN 130 NaN NaN NaN NaN d 18.0 88.0 130 33.0 129.0 NaN 117.0
2、 Series与DataFrame之间的运算
使用Python操作符:以行为单位操作(参数必须是行),对所有行都有效。(类似于numpy中二维数组与一维数组的运算,但可能出现NaN)
使用pandas操作函数:
axis=0:以列为单位操作(参数必须是列),对所有列都有效。
axis=1:以行为单位操作(参数必须是行),对所有行都有效。
df2 ----> DataFrame对象
s_row = df2.loc["c"] ---- Series对象
s_column = df2["Python"] ---- Series对象
df2.add(s_row) ---- df2对象每一列与s_row相加
df2.add(s_column, axis="index") ---- df2对象每一行与s_column相加
# axis参数,指定两者相加的方式,默认等于column
丢失数据的处理
分为两种: None np.nan(NaN)
None
numpy中:
type(None) ---- NoneType
None是Python自带的,其类型为python object。因此,### None不能参与到任何计算中。
object类型的运算要比int类型的运算慢得多
计算不同数据类型求和时间
%timeit np.arange(1e5,dtype=xxx).sum()
%timeit np.arange(1e6, dtype = int).sum()
3.21 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.arange(1e6, dtype = float).sum()
6.89 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.arange(1e6, dtype = object).sum()
89.3 ms ± 9.79 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
pands中:
pandas中将None视作np.nan
np.nan(NaN)
numpy中:
np.nan是浮点类型,能参与到计算中。但计算的结果总是NaN。
但可以使用np.nan*()函数来计算nan,此时视nan为0
nd = np.array([10,20,30,np.nan])
np.nansum(nd) ----> 60.0 # 将nan视为0
pandas中:
pandas中None与np.nan都视作np.nan
df = DataFrame([10,20,57,np.nan, None],index = list("abcde"), columns = ["Python"])
df.sum() ----> 87.0
df = DataFrame([[10,20,57,np.nan,None],
[22,33,56,12,None],
[np.nan,1,2,3,4]], index = list("abc"),
columns = ["Python","Java","数学","物理","H5"])
df.sum(axis=1) ----> 求每一行的和
pandas中None与np.nan操作
判断函数:
isnull():数据是否为空
notnull():数据是否不为空
#DataFrame的isnull函数返回的是和之前一模一样的结构,且为boolean类型,不为空的False 为空的True
isnull().any() -- 判断行或列中的数据是否有空值,有返回True,没有返回False
notnull().all() -- 判断行或列的数据是否全不为空,是返回True,否则返回False
用法:
df = DataFrame([[10,20,57,np.nan,None],
[22,33,56,12,None],
[np.nan,1,2,3,4]], index = list("abc"),
columns = ["Python","Java","数学","物理","H5"])
df2 = DataFrame([[10,20,57,90,20],
[22,33,56,12,80],
[100,1,2,3,4]], index = list("efg"),
columns = ["Python","Java","数学","物理","H5"])
df3 = df.add(df2, fill_value=0) ---- df+df2
df3_isnull = df3.isnull().any(axis = 1) ---- 判断df3每行是否有空值,返回Series类型
df3[df3_isnull] ---- 把非空值的行过滤掉,留下含空值的行
扩展:
cond = (df3 >= 10).all(axis = 1)
# 判断df3每一行数据是否全部都大于等于10,返回series类型对象,且值为布尔类型
df3[cond] ---- 取出df3中值全部大于等于10的数据
过滤函数:
dropna(): 过滤丢失数据
df3["H5"] = np.nan ---- DataFrame的列索引,把“H5”列全部赋值为np.nan
df3.dropna(axis = 1, how = "all") ---- 过滤掉值全为空的列
axis :可以选择过滤的是行还是列(默认为行,axis=0)
也可以选择过滤的方式 how = 'all'
填充函数:
fillna(): 填充丢失数据
df3.fillna(-1) ---- 将df3中为空的数据全部填充为-1
df3.fillna(method="bfill") ---- 向后填充(用下一行的数据填充)
df3.fillna(method="ffill") ---- 向前填充(用上一行的数据填充)
df3.fillna(method="ffill",axis = 1) ---- 向右进行填充(用左边的数据填充)
df3.fillna(method="bfill",axis = 1) ---- 向左进行填充(用右边的数据填充)
# method:可以选择前向填充还是后向填充
# 对于DataFrame来说,还要选择填充的轴axis;axis=0:index/行;axis=1:columns/列
pandas层次化索引
创建多层行索引
1、 隐式构造
最常见的方法是给DataFrame构造函数的index参数传递两个或更多的数组
Series也可以创建多层索引
s = Series(data = [1,2,3,"a"],
index = [["a","a","b","b"],["期中","期末","期中","期末"]])
DataFrame:
# 二层索引
df = DataFrame(data = [1,2,3,"a"],
index = [["a","a","b","b"],["期中","期末","期中","期末"]],
columns = ["Python"])
# 三层索引
df = DataFrame(data = np.random.randint(0,150,size = 8),
index = [['a',"a","a","a","b","b","b","b"],
['期中',"期中","期末","期末",'期中',"期中","期末","期末"],
["一单元","二单元","一单元","二单元","一单元","二单元","一单元","二单元"]],
columns = ["Python"])
2、 显示构造 pd.MultiIndex
1. 使用数组
df1 = DataFrame(data = np.random.randint(0,150,size = (8,4)),
index = pd.MultiIndex.from_arrays([['a',"a","a","a","b","b","b","b"],
['期中',"期中","期末","期末",'期中',"期中","期末","期末"],
["一单元","二单元","一单元","二单元","一单元","二单元","一单元","二单元"]]),
columns = ["Python","h5","php","go"])
2. 使用tuple
df3 = DataFrame(np.random.randint(0,150,size = 4),
index = pd.MultiIndex.from_tuples([("a",1),("a",2),("b",1),("b",2)]),
columns = ["Python"])
3. 使用product ---- 最简单,推荐使用
df4 = DataFrame(np.random.randint(0,150,size = (8,2)),
index = pd.MultiIndex.from_product([list("abcd"), ["期中","期末"]]),
columns = ['Python',"数学"])
多层列索引
除了行索引index,列索引columns也能用同样的方法创建多层索引
df = DataFrame(data = np.random.randint(0,150,size = (1,8)),
columns = [['a',"a","a","a","b","b","b","b"],
['期中',"期中","期末","期末",'期中',"期中","期末","期末"],
["一单元","二单元","一单元","二单元","一单元","二单元","一单元","二单元"]],
index = ["Python"])
多层索引对象的索引与切片操作
1、 Series的操作
# 对于Series来说,直接中括号[]与使用.loc()完全一样,因此,推荐使用中括号索引和切片。
s = Series(data=[1,2,3,"a"], index = [["a","a","b","b"],["期中","期末","期中","期末"]])
1. 索引
s["a"]["期末"] 或 s["a","期末"] ---- 效果相同
2. 切片
s["a":"b"] ---- 得到整个对象
s["期中":"期末"] ---- 空的对象
# 这两种方法得到的对象不精确,不推荐使用
s.iloc[0:3] ---- 隐式索引切片
# 把所有的数据放到一块进行切片,得到的数据比较精确 推荐使用
2、 DataFrame的操作
(1) 可以直接使用列名称来进行列索引
(2) 使用行索引需要用ix(),loc()等函数
【极其重要】推荐使用loc()函数
# 注意: 在对行索引的时候,若一级行索引还有多个,对二级行索引会遇到问题!也就是说,无法直接对二级索引进行索引,必须让二级索引变成一级索引后才能对其进行索引!
df4 = DataFrame(np.random.randint(0,150,size = (8,2)),
index = pd.MultiIndex.from_product([list("abcd"), ["期中","期末"]]),
columns = ['Python',"数学"])
df4["Python"]["a","期末"] ---- 先列索引,再行索引
df4.loc["a"].loc["期末"]["Python"] ---- 先行索引,再列索引
# 在索引取值的时候,一定要注意数据的类型
索引的堆
unstack():行索引变列索引
df4.unstack() ---- 把第二层的行索引变成列索引
df4.unstack(level=1) ---- 把第二层的行索引变成列索引
df1.unstack(level = 1) ---- 把df1的第二层行索引变为列索引
# 【小技巧】使用unstack()的时候,level等于哪一个,哪一个就消失,出现在列里。
stack(): 列索引变行索引
df.stack(level=1) ---- 把df的第二层列索引变成行索引
df.stack(level=(0,2)) ---- 把df的第一层、第三层列索引放到行索引上面
# 【小技巧】使用stack()的时候,level等于哪一个,哪一个就消失,出现在行里。
# level索引的下标(从0开始计数)最外层为0,第二层为1,以此类推
聚合操作
【注意】需要指定axis
【小技巧】和unstack()相反,聚合的时候,axis等于哪一个,哪一个就保留。
所谓的聚合操作:平均数,方差,最大值,最小值……
df4.div(10, axis = "index") ---- df4的数据除10
df4.sum(axis = 1) ---- 按行求和
df4.std() ---- df4的标准差
pandas的拼接操作
pandas的拼接分为两种:
级联:pd.concat, pd.append
合并:pd.merge, pd.join
使用pd.concat()级联
pandas使用pd.concat函数,与np.concatenate函数类似,只是多了一些参数:
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False,
copy=True) ---- 常用
# axis:轴,拼接方向,默认为行拼接
# ignore_index:是否忽视索引,默认不忽视
# keys:是否使用多层索引,默认不使用
# join:连接方式,默认为外链接;join=“inner”时内连接
# join_axes:连接指定轴
# 定义生成DataFrame的函数
def make_df(cols, inds):
#data: a,{a:[a1,a2,a3],b:[b1,b2,b3],c:[c1,c2,c3]}
data = {c:[c+str(i) for i in inds] for c in cols}
return DataFrame(data, index = inds)
df1 = make_df(list("abc"), [1,2,3])
df2 = make_df(list('abc'), [4,5,6])
df3 = make_df(list("def"), [1,2,3])
df4 = make_df(list("abc"), [2,3,4])
df5 = make_df(list("abcd"), [3,4,5,6])
df6 = make_df(list("abcz"), [3,4,7,8])
1、 简单级联
pd.concat([df1,df2],axis=0) ---- 行拼接(行数增加)
pd.concat([df1,df2],axis=1) ---- 列拼接(列数增加)
# 在行上面进行级联的时候,最好列相同,才不会出现空值
# 在列上面进行级联的时候,最好行相同,才不会出现空值
pd.concat([df1,df4], ignore_index=True) ---- 忽视索引,并重新索引
pd.concat([df1,df4], keys = ["一班","二班"]) ---- 使用多层索引
2、 不匹配级联
有三种连接方式:外链接、内连接、指定轴连接
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致
pd.concat([df1,df5], join = "outer") ---- 外链接(没有的会补NaN)
# 外链接,两个表的都要
pd.concat([df1,df5], join = "inner") ---- 内连接
# 内连接,只取两个表共有的字段数据
pd.concat([df1,df2,df5,df6],join_axes=[df6.columns]) ---- 以df6的列为连接轴
# 指定轴连接,只显示已指定的轴,其他的不显示, 没有数据的补NaN
3、 使用append()函数添加 -- 不常用
由于在后面级联的使用非常普遍,因此有一个函数append专门用于在后面添加
df5.append(df1) ---- 在df5后面拼接df1,空字段补NaN
使用pd.merge()合并
merge与concat的区别在于,merge需要依据某一共同的行或列来进行合并
使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并。
注意每一列元素的顺序不要求一致
1、 一对一合并
df1 = DataFrame({"age":[30,22,36],
"work":["tech","accounting","sell"],
"sex":["男","女","女"]}, index = list("abc"))
df2 = DataFrame({"home":["上海","安徽","山东"],
"work":["tech","accounting","sell"],
"weight":[60,50,55]}, index = list("abc"))
pd.concat([df1, df2],axis = 1)
2、 多对一合并 ----常用
df3 = DataFrame({"home":['深圳',"北京","上海","安徽","山东"],
"work":["tech","tech","tech","accounting","sell"],
"weight":[60,75,80,50,55]}, index = list("abcde"))
df1.merge(df3)
# 会根据相同的列,如tech,得到1*3个数据
3、 多对多合并 ---- 很少用
df5 = DataFrame({"age":[28,30,22,36],
"work":['tech',"tech","accounting","sell"],
"sex":["女","男","女","女"]}, index = list("abcd"))
df3.merge(df5)
# 会根据相同的列,如tech,得到3*2个数据
4、key的规范化
1. 使用on=显式指定哪一列为key,当有多个key相同时使用
df6 = DataFrame({'age':[30,22,37],
"work":['tech',"leader","sell"],
"hobby":["sixdog","playcat","diaofish"]},
index = list("abc"))
df5.merge(df6, on = "age",suffixes=["_总部","_分部"])
# on: 用来指定以那一列为主; suffixes:指定后缀名,用以区分
# 当列冲突时,即有多个列名称相同时,需要使用on=来指定哪一个列作为key,配合suffixes指定冲突列名 可以使用suffixes=自己指定后缀
2. 使用left_on和right_on指定左右两边的列作为key,当左右两边的key都不想等时使用
df7 = DataFrame({"年龄":[30,22,36],
"工作":['tech',"accounting","sell"],
"性别":["男","女","女"]}, index = list("abc"))
df5.merge(df7, left_on="work",right_on="工作") ---- 这个很重要
# left_on:指定左边(df5)的连接键; right_on:指定右边(df7)的连接键;以两个键来拼接
以原有的列新建一列
s = df5[["age"]]*1000 ---- 更改“age”列数值,并赋给s
s.columns = ["salary"] ---- 重命名
pd.concat([df5,s], axis = 1) ---- 使用concat新建一列
df5.merge(s, left_index=True, right_index=True) ---- 使用合并新建一列
# left_index(df5)和right_index(s) 如果为True的话,就以行索引进行合并
5、 内合并与外合并
内合并:只保留两者都有的key(默认模式)
df3.merge(df6, how = "inner")
外合并 how='outer':补NaN
df3.merge(df6, how = "outer")
# how: 指定合并方式,默认为内合并
左合并 how='left'
df3.merge(df6, how = "left")
# 左合并以左边为主, 左边的数据宁愿为空值也不能丢失, 右边的可以
右合并 how='right'
df3.merge(df6, how = "right")
# 以右边的为主
参数说明:
left与right:两个不同的DataFrame