Pandas之二维数组DataFrame

8,859 阅读8分钟

DataFrame是一个多维数据类型。因为通常使用二维数据,因此,我们可以将DataFrame理解成类似excel的表格型数据,由多列组成,每个列的类型可以不同。Series其实就是一列

因为DataFrame是多维数据类型,因此,DataFrame既有行索引,也有列索引。

DataFrame创建方式

我们可以使用如下的方式创建(初始化)DataFrame类型的对象(常用):

  • 二维数组结构(列表,ndarray数组,DataFrame等)类型。
  • 字典类型,key为列名,value为一维数组结构(列表,ndarray数组,Series等)。

说明:

  • 如果没有显式指定行与列索引,则会自动生成以0开始的整数值索引。我们可以在创建DataFrame对象时,通过index与columns参数指定。
  • 可以通过head,tail访问前 / 后N行记录(数据)。

# 使用二维数据结构创建DataFrame。没有指定行列索引,自动生成行列索引。都是从0开始的自然数
array1 = np.random.rand(3, 5)
df = pd.DataFrame(array1)
print(df)

# 错误,超过了2d。
# df_more_than2d = pd.DataFrame(np.random.rand(3, 3, 3))
          0         1         2         3         4
0  0.877072  0.941101  0.131574  0.056032  0.141660
1  0.129488  0.211658  0.786556  0.477778  0.912969
2  0.624839  0.336306  0.936274  0.581543  0.541653

# 使用字典来创建DataFrame。一个键值对为一列。key指定列索引,value指定该列的值。
df = pd.DataFrame({"北京":[100, 200, 125, 112], "天津":[109, 203, 123, 112], "上海":[39, 90, 300, 112]})
display(df)


# 显示前N条记录
display(df.head(2))
# 显示后N条记录
display(df.tail(2))
# 随机抽取N条记录
display(df.sample(2)) 


# 创建DataFrame,指定行,列索引。
df = pd.DataFrame(np.random.rand(3, 5), index=["地区1", "地区2", "地区3"], columns=["北京","天津", "上海","沈阳", "广州"])
display(df)


DataFrame相关属性

  • index 索引
  • columns 列
  • values 值
  • shape 形状
  • ndim 维度
  • dtypes 数据类型

说明:

  • 可以通过index访问行索引,columns访问列索引,values访问数据,其中index与columns也可以进行设置(修改)。
  • 可以为DataFrame的index与columns属性指定name属性值。
  • DataFrame的数据不能超过二维。

df = pd.DataFrame(np.random.rand(3, 5), index=["地区1", "地区2", "地区3"], columns=["北京","天津", "上海","沈阳", "广州"])
# 查看idnex, columns, values等属性
display(df.values, type(df.values))  # 返回DataFrame关联的ndarray数组
display(df.index)   # 返回行索引
display(df.columns)  # 返回列索引
array([[0.88915553, 0.09234275, 0.41773469, 0.92490647, 0.13286735],
       [0.85550017, 0.06293159, 0.75023895, 0.01887861, 0.327761  ],
       [0.13309605, 0.98347602, 0.95935583, 0.92139592, 0.48752687]])numpy.ndarrayIndex(['地区1', '地区2', '地区3'], dtype='object')Index(['北京', '天津', '上海', '沈阳', '广州'], dtype='object')

# 返回形状
display(df.shape)

# 返回维度
display(df.ndim)

# 返回各列的类型信息。
display(df.dtypes)
(3, 5)2北京    float64
天津    float64
上海    float64
沈阳    float64
广州    float64
dtype: object

DataFrame相关操作

假设df为DataFrame类型的对象。

列操作

  • 获取列【哪个更好些?】
    • df[列索引]
    • df.列索引
  • 增加(修改)列:df[列索引] = 列数据
  • 删除列
    • del df[列索引]
    • df.pop(列索引)
    • df.drop(列索引或数组)

行操作

  • 获取行
    • df.loc 根据标签进行索引。
    • df.iloc 根据位置进行索引。
    • df.ix 混合索引。先根据标签索引,如果没有找到,则根据位置进行索引(前提是标签不是数值类型)。【已不建议使用, 新版本废弃】
  • 增加行:append【多次使用append增加行会比连接计算量更大,可考虑使用pd.concat来代替】
  • 删除行
    • df.drop(行索引或数组)

行列混合操作:

  • 先获取行,再获取列。
  • 先获取列,在获取行。

说明:

  • drop方法既可以删除行,也可以删除列,通过axis指定轴方向。【可以原地修改,也可以返回修改之后的结果。】
  • 通过df[索引]访问是对列进行操作。
  • 通过df[切片]访问是对行进行操作。【先按标签,然后按索引访问。如果标签是数值类型,则仅会按标签进行匹配。】
  • 通过布尔索引是对行进行操作。
  • 通过数组索引是对列进行操作。

这个地方重点区分,极易混淆。
换种方式进行总结:

  • 行操作:切片 和 布尔数组
  • 列操作:索引 和 标签数组/位置数组

df = pd.DataFrame(np.random.rand(5, 5), columns=list("abcde"), index=list("hijkl"))
display(df)


# 获取多个列(返回一个DataFrame,即使只选择一个列)
display(df[["a", "d"]])


# 删除列
df["e"] = [6, 7, 8, 9, 10]
del df["e"]
df["e"] = [6, 7, 8, 9, 10]     # 把e列加回去
display(df.pop("e"))           # 从df中删除e列,会返回删除的列e
display(df)


h     6
i     7
j     8
k     9
l    10
Name: e, dtype: int64


df2 = df.drop("h", inplace=False, axis=0)
display(df, df2)


# 先构造一个DataFrame
np.random.seed(100)
df = pd.DataFrame(np.random.rand(5, 5), index=list("abcde"), columns=list("yuiop"))
display(df)


# 先获取行,再获取列, 其实loc是从高维到低维逐渐定位的。
display(df.loc["c"]["i"])
display(df.loc["c", "i"])
display(df.loc["c"].loc["i"])
display(df.loc["c"])    # loc函数是从高维到低维依次定位的。不能不指定高维,直接定位到低维
display(df.loc[:, "i"])
0.185328219550075060.185328219550075060.18532821955007506y    0.891322
u    0.209202
i    0.185328
o    0.108377
p    0.219697
Name: c, dtype: float64a    0.424518
b    0.825853
c    0.185328
d    0.171941
e    0.817649
Name: i, dtype: float64

# 先获取列,在获取行。
df["i"].loc["a"] = 3
display(df)

# 标签索引组定位列,然后loc切片行
display(df[["i", "o", "p"]].loc["b":"d"])


# 如果布尔数组是二维结构,则True对应的位置元素原样显示,False对应位置的元素置为空值(NaN)
display(df > 0.5)
display(df[df > 0.5])
display(df["i"] > 0.5)
display(df[df["i"] > 0.5])


df = pd.DataFrame(np.random.rand(5, 5), columns=list("abcde"), index=list("hijkl"))
display(df)
# DataFrame行操作
# 获取行 loc   iloc   ix
# loc 根据标签获取
# iloc 根据位置获取

# ix 混合索引  先根据标签,然后再根据位置
display(df.loc["i"])
display(df.iloc[1])

# 不建议使用,因为非常容易造成混淆。
display(df.ix["i"])
display(df.ix[1])


a    0.598843
b    0.603805
c    0.105148
d    0.381943
e    0.036476
Name: i, dtype: float64
a    0.598843
b    0.603805
c    0.105148
d    0.381943
e    0.036476
Name: i, dtype: float64
a    0.598843
b    0.603805
c    0.105148
d    0.381943
e    0.036476
Name: i, dtype: float64
a    0.598843
b    0.603805
c    0.105148
d    0.381943
e    0.036476
Name: i, dtype: float64

总结

1)选取某一整行(多个整行)或某一整列(多个整列)数据时,可以用df[]、df.loc[]、df.iloc[],此时df[]的方法书写要简单一些。

2)进行区域选取时,如果只能用标签索引,则使用df.loc[]或df.ix[],如果只能用整数索引,则用df.iloc[]或df.ix[]。不过我看到有资料说,不建议使用df.ix[],因为df.loc[]和df.iloc[]更精确(有吗?我没理解精确在哪,望告知)。

3)如果选取单元格,则df.at[]、df.iat[]、df.loc[]、df.iloc[]都可以,不过要注意参数。  

4)选取数据时,返回值存在以下情况:

如果返回值包括单行多列或多行单列时,返回值为Series对象; 如果返回值包括多行多列时,返回值为DataFrame对象; 如果返回值仅为一个单元格(单行单列)时,返回值为基本数据类型,例如str,int等。

5)df[]的方式只能选取行和列数据,不能精确到单元格,所以df[]的返回值一定DataFrame或Series对象。

6)当使用DataFrame的默认索引(整数索引)时,整数索引即为标签索引。

DataFrame 增删改

df = pd.DataFrame(np.random.rand(5, 5), columns=list("abcde"), index=list("hijkl"))
display(df)

# 增加一行
line = pd.Series([23, 33, 12., 334.22, 200], index=list("abcde"), name="p")
df = df.append(line)
display(df)

# 删除一行
df1 = df.drop(["h", "j"])
display(df1)

# 修改一行
df.loc["k"] = pd.Series([1,1,1,1,1], index=list("abcde"))
display(df)



DataFrame结构

DataFrame的一行或一列,都是Series类型的对象。
对于行来说,Series对象的name属性值就是行索引名称,其内部元素的值,就是对应的列索引名称。
对于列来说,Series对象的name属性值就是列索引名称,其内部元素的值,就是对应的行索引名称。

DataFrame运算

DataFrame的一行或一列都是Series类型的对象。因此,DataFrame可以近似看做是多行或多列Series构成的,Series对象支持的很多操作,对于DataFrame对象也同样适用,我们可以参考之前Series对象的操作。

  • 转置
  • DataFrame进行运算时,会根据行索引与列索引进行对齐。当索引无法匹配时,产生空值(NaN)。如果不想产生空值,可以使用DataFrame提供的运算函数来代替运算符计算,通过fill_value参数来指定填充值。
  • DataFrame与Series混合运算。【默认Series索引匹配DataFrame的列索引,然后进行行广播。可以通过DataFrame对象的运算方法的axis参数,指定匹配方式(匹配行索引还是列索引)。】

df1 = pd.DataFrame(np.arange(9).reshape(3, 3))
df2 = pd.DataFrame(np.arange(9, 18).reshape(3, 3))
display(df1, df2)
display(df1 + df2)
display(df1 * df2)


索引排序

Series与DataFrame对象可以使用sort_index方法对索引进行排序。DataFrame对象在排序时,还可以通过axis参数来指定轴(行索引还是列索引)。也可以通过ascending参数指定升序还是降序。

df = pd.DataFrame(np.random.random((3, 5)), index=[3,1,2], columns=[1,3,5,2,4])
display(df)


# 指定按照行索引排序
display(df.sort_index(axis=0, ascending=False))


# 指定按照列索引排序
display(df.sort_index(axis=1, ascending=True))


df = pd.DataFrame(np.arange(9).reshape(3, 3), index=[3, 1, 2], columns=[6, 4, 5])
display(df)
# 默认根据行索引进行排升序。
df1 = df.sort_index()
display(df1)
# 根据列索引进行排序
df2 = df.sort_index(axis=1)
display(df2)

      

# 就地修改,不会返回修改后的结果。
df.sort_index(inplace=True)
display(df)
# 默认为升序排序,可以指定为降序排序。
df3 = df.sort_index(ascending=False, axis=1)
display(df3)

   

值排序

Series与DataFrame对象可以使用sort_values方法对值进行排序。

df = pd.DataFrame([[1,3, 2], [5, 2, 4], [2, 4, 3]], index=[1,3,2], columns=list("cab"))
display(df)

# 按照列排序,指定顺序
df1 = df.sort_values("c", ascending=False)
display(df1)

# 按照行排序,指定顺序
df2 = df.sort_values(1, axis=1, ascending=False)
display(df2)

# 值排序
df = pd.DataFrame([[1, 3, 300], [66, 5, 100], [1, 3, 400]])
display(df)

# 根据第2列排升序
df1 = df.sort_values(2)
display(df1)

# 根据第1行排降序
df2 = df.sort_values(1, axis=1, ascending=False)
display(df2)

DataFrame索引对象Index

Series(DataFrame)的index或者DataFrame的columns就是一个索引对象。

  • 索引对象可以向数组那样进行索引访问。
  • 索引对象是不可修改的。

DataFrame统计相关方法

  • mean / sum / count
  • max / min
  • cumsum / cumprod
  • argmax / argmin
  • idxmax / idxmin
  • var / std