Python科学计算:pandas

原文链接:https://wklchris.github.io/Py3-pandas.html

pandas 是 Python 下科学计算非常实用的一个工具。本文将简明扼要地介绍其使用方法。如果想要初步地操作数据,直接阅读本文即可;否则请先学习 numpy 相关的内容。

引言:Series 与 DataFrame

加载 pandas 时,一般也会同时加载 numpy. 默认使用如下的语句:

import numpy as np
import pandas as pd
from pandas import Series, DataFrame

pandas 内置了两种独特的数据结构:Series 与 DataFrame.

系列(Series)

系列是由多个同类型元素组成的有序列向量,有些接近 Python 原生的列表(list)。不同的是,系列包含各元素的一个索引:这个索引可以任意重命名。

s = Series([1, 2, np.nan, 4])
s
0    1.0
1    2.0
2    NaN
3    4.0
dtype: float64
s.index = list('abcd')
s
a    1.0
b    2.0
c    NaN
d    4.0
dtype: float64
s["c"]
nan

数据框(DataFrame)

数据框是一个二维的数据结构,特点是:

  • 列内的元素同类型,不同的列之间可以不相同。

  • 索引有两个轴向:axis=0/”index”行,axis=1/”columns” 列。分别用 df.index(行名)与df.columns(列名)调用。

在数据处理中,使用数据框是非常便捷的;而系列我们却很少使用。

数据读写

数据读取

pandas 内置的文件读取函数:

  • pd.read_csv():参数如下。

    • names:指定表头(df.columns)。

    • header:指定整数 n,表示前 n 行会被读作表头。如果 names 指定了,会被设为 0.

    • engine: “c” 或者 “python”。前者会快一些。

    • skipinitialspace: 是否忽略紧跟在分隔符后的空格。默认值 False.

    • na_values:指定一个字符串列表,里面的字符串都会被识别为 NaN。默认的有 [‘’, ‘#N/A’, ‘#N/A N/A’, ‘#NA’, ‘-1.#IND’, ‘-1.#QNAN’, ‘-NaN’, ‘-nan’,‘1.#IND’, ‘1.#QNAN’, ‘N/A’, ‘NA’, ‘NULL’, ‘NaN’, ‘nan’.]

    • na_filter:默认值 True。如果确定文件中不含 NaN,那么指定它为 False 可以提高大文件读取速度。 -skip_blank_lines:是否跳过空白行。默认值 True.

  • pd.read_table():只要是分隔符文件即可。参数 sep 缺省值是制表符。

  • pd.read_excel()

以上读取的结果都是 DataFrame. 其他的读取函数不再介绍。

数据写入

  • df.to_csv()

  • df.to_excel()

数据创建

以下介绍几种数据框的创建方法。第一种,如果是全数字的数据结构,可以直接从 numpy 中的矩阵创建。

# 参数 index 与 columns 是可选的
arr = np.arange(1, 19).reshape([6, 3])
df = DataFrame(arr, index=list('ABCDEF'), columns=list('ZYX'))
df

ZYX
A123
B456
C789
D101112
E131415
F161718

第二种,通过字典创建。字典的每个键下的值都是格式相同的列表。注意:此时的列会自动根据列名(原字典中的键)从左到右升序排列。

df = DataFrame({"Letters": ["A", "B"], "Age": [12, 34]})
df

AgeLetters
012A
134B

数据框尺寸:df.shape 与 len()

与 numpy 的矩阵类似,数据框拥有属性 shape,返回结果是一个元组:

df = DataFrame(arr, index=list('ABCDEF'), columns=list('ZYX'))
df.shape
(6, 3)

如果使用 Python 原生的 len() 函数,会返回数据框的行数。注意:如果你要基于数据框写循环语句,请利用它们的行名(df.index)而不是利用 len() 函数。这是因为数据框在切片时会保留行名索引;如果简单地使用 for row in range(len(...)) ,会出现 row 与行名不一致的问题。

len(df)
6

预览数据:df.head() / tail() / values

常用的是前两个,用于查看数据框头部 5 行或者尾部 5 行的数据。你也可以传入数字指定查看的行数。

df.head(3)

ZYX
A123
B456
C789
df.tail()

ZYX
B456
C789
D101112
E131415
F161718

df.values 则是一个有些怪异的命令:它会返回一个 np.array 类型的数据。

df.values
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12],
       [13, 14, 15],
       [16, 17, 18]])

查看 df 的基础信息,使用 df.info() 命令:

df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 6 entries, A to F
Data columns (total 3 columns):
Z    6 non-null int32
Y    6 non-null int32
X    6 non-null int32
dtypes: int32(3)
memory usage: 96.0+ bytes

统计信息:df.describe(), s.value_counts() / unique()

df.describe()

ZYX
count6.0000006.0000006.000000
mean8.5000009.50000010.500000
std5.6124865.6124865.612486
min1.0000002.0000003.000000
25%4.7500005.7500006.750000
50%8.5000009.50000010.500000
75%12.25000013.25000014.250000
max16.00000017.00000018.000000

value_counts() 是一个适用于 Series 的命令。参数 dropna 可以使用。

df.apply(Series.value_counts, dropna=False)

ZYX
11.0NaNNaN
2NaN1.0NaN
3NaNNaN1.0
41.0NaNNaN
5NaN1.0NaN
6NaNNaN1.0
71.0NaNNaN
8NaN1.0NaN
9NaNNaN1.0
101.0NaNNaN
11NaN1.0NaN
12NaNNaN1.0
131.0NaNNaN
14NaN1.0NaN
15NaNNaN1.0
161.0NaNNaN
17NaN1.0NaN
18NaNNaN1.0

unique() 命令有时会配合集合(set)数据结构一同使用:

tmp = Series([1, 2, 3, np.nan, 14, 3, 1])
set_1 = set(tmp.unique())
set_1
{nan, 1.0, 2.0, 3.0, 14.0}

更改列类型:pd.to_numeric()

pandas 在读入数据时会自动识别各列的类型。识别的类型可以使用 dtypes 属性:

df.dtypes
Z    int32
Y    int32
X    int32
dtype: object

例如,我们将前两列的属性改为字符串:(这里使用的 applymap 函数会在后面提到)

df[["Z", "Y"]] = df[["Z", "Y"]].applymap(str)
df.dtypes
Z    object
Y    object
X     int32
dtype: object
# Check it
df["Z"][1]
'4'

对于上述的问题,astype() 命令也是一个解决方案:

df[["Z", "Y"]] = df[["Z", "Y"]].astype(str)

如果想转换为数字,pandas 提供了 to_numeric 命令。此命令是作用于 Series 的,因此你可能需要 apply 命令来应用于多个列:

df[["Z", "Y"]] = df[["Z", "Y"]].apply(pd.to_numeric)
df.dtypes
Z    int64
Y    int64
X    int32
dtype: object

注意到该命令有个 errors 参数,用于处理无法正常转换的情况。可以指定以下值:

  • “ignore”: 如果列中存在无法转换的元素,那么整个列不作转换;

  • “coerce”:如果列中存在无法转换的元素,仍然转换列,并将这些元素转换为 NaN

tmp = Series([1, 2, "Tree", 4])
pd.to_numeric(tmp, errors="ignore")
0       1
1       2
2    Tree
3       4
dtype: object
pd.to_numeric(tmp, errors="coerce")
0    1.0
1    2.0
2    NaN
3    4.0
dtype: float64

更改行 / 列名:df.rename()

数据框的行名 / 列名列表分别用 df.columns 与 df.index 查看。

df.columns
Index(['Z', 'Y', 'X'], dtype='object')
df.index
Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')

因此,最简单粗暴的行名 / 列名更改方法是:将一个列表赋值过去。

df.columns = list("RQP")
df.head(1)

RQP
A123

有时需要只更改几个名称,难道需要创建整个列表吗?这时候使用 df.rename 命令,通过字典的方式替换:

# 在很多函数中,inplace 参数都避免了自赋值的操作
df.rename(columns = {"R": "Z", "Q": "Y", "P": "X"}, inplace=True)
df.head(1)

ZYX
A123

数据整理

深度复制:df.copy()

不用多说。如果不进行深度复制,df 与 df2 将会是同址的。

df2 = df.copy()

关于切片时的深度复制问题,会在介绍切片时阐述。

转置:df.T

df.T  # 不改变 df 

ABCDEF
Z147101316
Y258111417
X369121518

排序

排序分为两种:

  1. 按照列名/行名的字母顺序排序列/行,这里称为索引重排

  2. 按照列内/行内的数据间大小关系,排序整个数据框的行/列,这里称为(数据)排序

索引重排:df.sort_index()

默认参数是 axis=0(重排行),ascending=True(升序)。支持 inplace 参数。

df.sort_index(ascending=False)

ZYX
F161718
E131415
D101112
C789
B456
A123
df.sort_index(axis="columns")  # 或 axis = 1

XYZ
A321
B654
C987
D121110
E151413
F181716
数据排序:df.sort_values()

此时只能根据列来排序。参数 na_position 默认值是 “last”,也可以指定 “first”.

df.sort_values(by="Z", ascending=False)

ZYX
F161718
E131415
D101112
C789
B456
A123

切片与选取

普通选取
  • 选取单列:df['X'] 或者 df.X

  • 选取多列:df[['X', 'Y']]

  • 选取行:例如前两行 df[:2]

  • 按照标签:df.loc['A'] / df.loc[['A', 'E'], 'X']

  • 按照索引序号:第三行 df.iloc[2]df.iloc[:2, :1]

  • 单元素选取:df.at / df.iat,只能选取单个元素。

在多数情况下,请尽量使用 loc/iloc 命令。它们是深度复制命令,这样可以避免多数的引用问题。

布尔型选取
df[(df.X > 10) & (df.Y < 15)]

ZYX
D101112
E131415
df[df > 10]  # 对整个 df 进行判断,不符合的填充 NaN

ZYX
ANaNNaNNaN
BNaNNaNNaN
CNaNNaNNaN
DNaN11.012.0
E13.014.015.0
F16.017.018.0
df[df.X.isin([3, 6, 8])]  # 根据列 X 中是否有列表里对应元素,筛选行

ZYX
A123
B456

缺失数据:df.dropna() / fillna()

df.dropna() 将筛选含有 NaN 的行/列(用参数 axis 指定),支持 inplace 参数。参数 how 指定筛选的方式:

  • df.dropna(how=”any”):行/列中存在 NaN 就抛弃。

  • df.dropna(how=”all”):只有当行/列全为 NaN 时才抛弃。

参数 thresh 指定了行/列中最低应存在的非 NaN 元素个数;如果非 NaN 元素个数小于这一阈值,该行/列会被抛弃。

df.fillna() 将 NaN 替换为另外的数值,用参数 value 指定。例如:df.fillna(value=-1)

pd.isnull() 命令将会返回一个布尔型数据框,检验各元素是否为 NaN。

pd.isnull(df).head(2)

ZYX
AFalseFalseFalse
BFalseFalseFalse

查找替换:df.replace()

df.replace([1, 2], [100, 200])

ZYX
A1002003
B456
C789
D101112
E131415
F161718

合并与分组

合并:df.append() / join(), pd.concat()

append() 是源自 list 类型的函数,类似 list 类型的 DataFrame 也可以调用。其作用是行合并。

df1 = df
df2 = df.rename(columns={"X": "Z", "Z": "P"})
df1.append(df2)

PXYZ
ANaN3.021
BNaN6.054
CNaN9.087
DNaN12.01110
ENaN15.01413
FNaN18.01716
A1.0NaN23
B4.0NaN56
C7.0NaN89
D10.0NaN1112
E13.0NaN1415
F16.0NaN1718

pd.concat() 则是一个可以指定合并轴的函数:

pd.concat([df1, df2], axis="columns")  # 列合并

ZYXPYZ
A123123
B456456
C789789
D101112101112
E131415131415
F161718161718

如果按默认的合并方式 join=”outer”,那么 concat(axis="index") 的结果与 append() 一致。但你可以指定内联合并:

pd.concat([df1, df2], axis="index", join="inner")

YZ
A21
B54
C87
D1110
E1413
F1716
A23
B56
C89
D1112
E1415
F1718

df.join() 则允许你使用类似 SQL 的方法进行合并。利用其参数 how 与 on 等可以控制合并方式。

分组:df.groupby()

df.groupby() 函数与 R 语言中的 aggregate() 有异曲同工之妙。

dt = DataFrame({"A": [1, 2, 3, 4, 5, 6],
                "B": ["a", "a", "a", "b", "b", "b"],
                "C": ["x", "x", "y", "y", "z", "z"], 
                "D": [1, 1, 1, 2, 2, 2]})
dt.groupby("B")
<pandas.core.groupby.DataFrameGroupBy object at 0x063332D0>

返回值无法直接理解,需要配合统计函数使用。可以参考本文“统计函数”一节的内容。

dt.groupby("B").mean()  # 列 C 无法计算 mean(),被忽略

AD
B

a21
b52

也可以同时应用到多个列。如果你了解过 R 语言中“因子”的概念,会对该函数的理解有所帮助。列 B 有两个水平,列 C 有三个。

dt.groupby(["B", "C"]).mean()


AD
BC

ax1.51.0
y3.01.0
by4.02.0
z5.52.0

groupby() 函数默认会将函数应用到所有其他的列。如果你只想计算指定的列,那么:

dt.groupby(["B", "C"])["A"].mean()
B  C
a  x    1.5
   y    3.0
b  y    4.0
   z    5.5
Name: A, dtype: float64

函数式应用:df.apply()/applymap()

df.apply() 函数在前面已经介绍过。参数主要是 axis,以及也能继承要应用的函数的参数。所有可以应用于 Series 的函数都能够这样应用到不同的列上。这里再次使用上文使用过的 Series.value_counts() 函数。

df.apply(Series.value_counts, dropna=False).head(4)

ZYX
11.0NaNNaN
2NaN1.0NaN
3NaNNaN1.0
41.0NaNNaN

一个用 df.applymap() 实现的小数位数处理,保留到第二位:

df.applymap(lambda x: "%.2f" % x)

ZYX
A1.002.003.00
B4.005.006.00
C7.008.009.00
D10.0011.0012.00
E13.0014.0015.00
F16.0017.0018.00

统计函数

除了上文介绍过的 df.describe(),以下运算均以“列”为基本单位:

  • df.mean()

  • df.corr()

  • df.count():非 NaN 数据计数

  • df.max()/df.minx()

  • df.median()

  • df.std()

猜你可能喜欢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值