pandas之DataFrame

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的运算

1DataFrame之间的运算
同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
 
2Series与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"])
多层索引对象的索引与切片操作

1Series的操作
# 对于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] ---- 隐式索引切片
   # 把所有的数据放到一块进行切片,得到的数据比较精确 推荐使用
2DataFrame的操作
(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
how:指的是合并(连接)的方式有inner(内连接),left(左外连接),right(右外连接),outer(全外连接);默认为inner
on : 指的是用于连接的列索引名称。必须存在右右两个DataFrame对象中,如果没有指定且其他参数也未指定则以两个DataFrame的列名交集做为连接键
left_on:左则DataFrame中用作连接键的列名;这个参数中左右列名不相同,但代表的含义相同时非常有用。
right_on:右则DataFrame中用作 连接键的列名
left_index:使用左则DataFrame中的行索引做为连接键
right_index:使用右则DataFrame中的行索引做为连接键
sort:默认为True,将合并的数据进行排序。在大多数情况下设置为False可以提高性能
suffixes:字符串值组成的元组,用于指定当左右DataFrame存在相同列名时在列名后面附加的后缀名称,默认为('_x','_y')
案例补充扩展

案例:美国各州人口数据分析(具体操作见jupyter day04)


删除指定列:drop()
例:pop2.drop("abbreviation", axis = 1, inplace=True)
   # axis: 指定轴(axis=0,对行操作; axis=1,对列操作)
   # inplace: 是否在原对象上操作,默认为False,不改变原数据;inplace=True,在原对象上修改
去重操作:unique()
例:pop2[cond_state]["state/region"].unique()

pandas数据处理

删除重复元素

duplicated()
使用duplicated()函数检测重复的行,返回元素为布尔类型的Series对象,每个元素对应一行,如果该行不是第一次出现,则元素为True
df = DataFrame({"color":["red","pink","red","green"], "size":[10,20,10,30]})
df.duplicated()
使用drop_duplicates()函数删除重复的行
df.drop_duplicates()
映射

映射的含义:创建一个映射关系列表,把values元素和一个特定的标签或者字符串绑定
需要使用字典:
map = {
   'label1':'value1',
   'label2':'value2',
  ...
  }
包含三种操作:
replace()函数:替换元素
最重要:map()函数:新建一列
rename()函数:替换索引
1replace()函数:替换元素
使用replace()函数,对values进行替换操作
   首先定义一个字典:color = {'red':10, "green":20}
   调用.replace():df.replace(color, inplace=True)
   # inplace:是否修改原对象,默认为False,不修改原对象
   replace还经常用来替换NaN元素
   例:  df.loc[1] = np.nan
         v ={np.nan:40}
df.replace(v)
2map()函数:新建一列
 1. 使用map()函数,由已有的列生成一个新列
适合处理某一单独的列。
df = DataFrame(np.random.randint(0,150,size = (4,4)),columns =["Python","Java","PHP","HTML"],index = ["张三","旭日","阳刚","木兰"])
   仍然是新建一个字典
   v = {36:37,54:105,117:108,144:124}
   df["Go"] = df["Python"].map(v) -- 利用python列新建Go列
 2. map()函数中可以使用lambda函数
   df["C"] = df["Go"].map(lambda x: x-10)
 3. map()函数还可以传 一个回调函数
   def mp(x):
  #复杂的条件
  if x < 51:
      return "不及格"
  else:
      return "优秀"
   df["score"] = df["C"].map(mp)
3transform()
transform()用法和map()类似

rename()函数:替换索引
def cols(x):
   if x == "Python":
       return "大蟒蛇"
   if x == "PHP":
       return "php"
   else:
       return x
df.rename(columns=cols) ----传回调函数
inds = {"张三":"Zhang sir","木兰":"Miss hua"}
df.rename(index=inds) ---- 传字典

 

posted on 2020-11-08 15:38  懒得显示  阅读(494)  评论(0编辑  收藏  举报