目录
一、Pandas简介
大数据虽然描述的是海量的数据,但是大数据离你却并不远,特别是大数据所涵盖的技术,在你生活当中,是时刻都能使用这些大数据涉及到的技术, 来解决你生活中的具体问题。
- 办公自动化
- 上学上班,有 Excel 或者格式化的文本文件,需要进行数据加工处理
- 对大量的这些文本文件作图,想要自动化处理
- 人工智能
- 数据分析,可视化数据规律
- 数据前处理,为 AI 模型展平道路
Pandas 是什么
简单来说,Pandas 是 Python 中一个比较常用的第三方库,里面集成了很多和数据相关的功能组件。上面提到的那些场景中, 无非就是要:
- 处理数据
- 分析数据
- 画图
所以 Pandas 就围绕着这些环节施展能力。而且它承接了 Numpy 的能力,使用的底层也是 Numpy。按理来说,我们也能用 Numpy 来实现上述功能。
Pandas安装
cmd:pip install Pandas
Pandas 和 Numpy 的差别
Pandas 是在 Numpy 上的封装。 继承了 Numpy 的所有优点,但是这种封装有好有坏,我们在这节内容中就先来阐述一下 Pandas 和 Numpy 的对比
类比 Python
用过 Python,你肯定熟悉里面的 List和Dictionary, 我比较常拿这两种形态来对比 Numpy 和 Pandas 的关系
a_list = [1,2,3]
a_dict = {"a": 1, "b": 2, "c": 3}
print("list:", a_list)
print("dict:", a_dict)
上面就是一种最常见的 Python 列表和字典表达方式。而下面,我们展示的就是 Numpy 和 Pandas 的一种构建方式
import pandas as pd
import numpy as np
a_array = np.array([
[1,2],
[3,4]
])
a_df = pd.DataFrame(
{"a": [1,3],
"b": [2,4]}
)
print("numpy array:\n", a_array)
print("\npandas df:\n", a_df)
你会发现,我们看到的结果中,Numpy 的是没有任何数据标签信息的,你可以认为它是纯数据。而 Pandas 就像字典一样,还记录着数据的外围信息, 比如标签(Column 名)和索引(Row index)。 这也是我为什么总说 Numpy 是 Python 里的列表,而 Pandas 是 Python 里的字典
还是回到之前提的问题,对于数据运算,既然我们有了 Numpy,为什么还要用 Pandas?。对比列表和字典,我们很容易感受到其中的一种原因。 就是 Pandas 帮我们记录的信息量变多了
在Numpy中,如果你不特别在其他地方标注,你是不清楚记录的这里边记录的是什么信息的。而 Pandas 记录的信息可以特别丰富, 你给别人使用传播数据的时,这些信息也会一起传递过去。或者你自己处理数据时对照着信息来加工数据,也会更加友善。
这就是在我看来 Pandas 对比 Numpy 的一个最直观的好处。
另外 Pandas 用于处理数据的功能也比较多,信息种类也更丰富,特别是你有一些包含字符的表格,Pandas 可以帮你处理分析这些字符型的数据表。 当然还有很多其它功能,比如处理丢失信息,多种合并数据方式,读取和保存为更可读的形式等等。
这些都让 Pandas 绽放光彩 但是,Pandas 也有不足的地方:运算速度稍微比 Numpy 慢
你想想,因为 Pandas 是在 Numpy 之上的一层封装,所以肯定在处理数据的时候要多几层处理,小数据量的处理不要紧,慢一点就慢一点, 你也感受不到处理速度的变化。但当数据量变大,用 Numpy 要处理 1 小时的数据,你可能用 Pandas 要花两小时。 所以你得依据自己的实际需求来选择到底是用 Numpy 还是 Pandas。
如果在做少量数据的分析时,因为不涉及到机器学习的模型运算等,我都可以用 Pandas,但如果要模型训练, 训练过程中还一直要调用数据处理的功能, 我肯定毫不犹豫都用 Numpy 来做
总结
Pandas 是 Numpy 的封装库,继承了 Numpy 的很多优良传统,也具备丰富的功能组件,但是你还是得分情况来酌情选择要使用的工具
二、基本操作
从文件读取数据
- Excel文件
pd.read_excel()
df.to_excel()
- csv或txt等纯文本文件
pd.read_csv()
df.to_csv()
- 其他
pd.read_clipboard()
pd.read_html()
Pandas中的数据是什么
简单来说,Pandas 支持最好的是一维和二维数据,一维数据就是一个序列,一条数据,而二维数据是我们生活中更常见的种类,基本上所有 Excel 数据, 都是二维数据,有横纵交替,用两个维度来定位这个数据
- 数据序列Series
- 创建
- 转换 Numpy
- 数据表DataFrame
- 创建
- 转换 Numpy
数据序列Series
一串 Python List 的形式你肯定不陌生,Pandas 中的 Series 的核心其实就是一串类似于 Python List 的序列。只是它要比 Python List 丰富很多, 有更多的功能属性
import pandas as pd
l = [11,22,33]
s = pd.Series(l)
print("list:", l)
print("series:", s)
打印出来,对比很明显,Pandas Series 还帮我们额外维护了一份索引。有这个索引有啥意义呢?Python List 不也有一个隐藏的序号索引吗? 其实啊,Pandas 之所以做这一种索引,目的并不是仅让你用 0123 这样的序号来检索数据,它还想让你可以用自己喜欢的索引来检索。看看下面的代码吧
s = pd.Series(l, index=["a", "b", "c"])
只要是有索引形式的结构,都可以搞成 Series?比如下面这样
s = pd.Series({"a": 11, "b": 22, "c": 33})
既然 Python 的 List 可以用来创建 Series,那我想 Numpy 应该也可以吧,要不来试试
import numpy as np
s = pd.Series(np.random.rand(3), index=["a", "b", "c"])
既然 Numpy 和 List 可以用来创建 Series,那 Series 能回退到 Numpy array 或者 List 吗? 我们试一试
print("array:", s.to_numpy())
print("list:", s.values.tolist())
还真是,他们真的能自由穿梭,这可大大减轻了我们在不同的数据形式中切换的成本
数据表DataFrame
Pandas 首先支持的是序列数据和表格数据,因为这两种是如常生活中最常用的数据保存和编辑格式了,你见过有人去编辑一个 3 维数据吗? 话说回来,如果你硬扯上 Excel 里面的不同 sheet,把 sheet 当成第三个维度,那也是成立的。sheet 这个我就先不说了,我们先看看用 Pandas 的 DataFrame 怎么维护一张数据表吧。
举个最简单的例子。将一个二维数组变成 Pandas 的 DataFrame
df = pd.DataFrame([
[1,2],
[3,4]
])
df
它创建出来的 df
,在真实数据外圈,还包上了一层其他的数据,比如 0 1
, 这是什么呢?其实这是 DataFrame 中,用来索引行列的序号, 外圈的横竖都是 0 1
代表着每个维度上,都有第 0 位和第 1 位。如果我要按位置来选取一下其中的值,我可以这么干
# 第 0 行,第 1 列
# 或 第一个维度中的第 0 号,第二个维度中的第 1 号
df.at[0, 1]
上面我们见识到了直接用一个二维列表来创建 DataFrame,但是自动创建的索引序号并不是很可读。我们还能将这些序号换成人类更好理解的文字标签信息
df = pd.DataFrame({"col1": [1,3], "col2": [2, 4]})
df
可以从结果看出,其实字典中的 key 会被当成是数据中的 column,而 value 会被当做是 row,这个非常符合你在 Excel 中的使用习惯。 因为往往随着数据量变大,你用鼠标滚轮滚动查看不同数据的时候,天然的比较喜欢上下查看不同的数据样本,而不是左右查看,所以一般都是左右记的是数据标签(特征), 上下排列的是不同数据样本
见识了字典变 DataFrame,其实 Series 也是可以组合变成 DataFrame 的,而且这也非常符合常理, 如果我从 DataFrame 中取出一个 Column, 这不就变成了一条 Series 了吗?
print(df["col1"], "\n")
print("取出来之后的 type:", type(df["col1"]))
那么将两个 Series 拼接起来呢
df = pd.DataFrame({"col1": pd.Series([1,3]), "col2": pd.Series([2, 4])})
df
前面 Series 说过,我们是可以为它构建特殊索引的,现在来看看 Series 和 DataFrame 构建索引的方式:
s = pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"])
df = pd.DataFrame({"col1": [1,3], "col2": [2, 4]}, index=["a", "b"])
print(s, "\n")
print(df)
既然 DataFrame 的 Column 和 Index 这么有意思,十有八九,你会想取出来用一用这些 Column 和 Index,比如你数据比较大的时候,想初步看看这份数据涉及了多少特征, 数据的 index 有多少种的时候,你可以直接获取到这些信息
print(df.index, "\n")
print(df.columns)
现在有了 Pandas 中的 DataFrame,我们可以转化成 Numpy
df = pd.DataFrame({"col1": [1,3], "col2": [2, 4]}, index=["a", "b"])
df.to_numpy()
总结
Pandas 中,为了我们提供了日常最常用的数据存储方式,分别是 Series 的一维数据,和 DataFrame 的二维数据,在机器学习中,我们常会接触到 3 维甚至是更高维度, 但是在分析数据的时候,特别是,要结合 Excel 来分析数据的时候,二维数据才是最常用的。
选取数据
多种选取方式
在 Pandas 中,有丰富的选取数据方式,这可比 List,Dictionary,甚至是 Numpy 还要多样化。我们既能通过文字标签来定位数据,也能通过数值序号来定位。 所以为了实现这种多样性,Pandas 对于数据的选取采用了不同类型的处理方法,比如 .loc
, .iloc
等
面对应用比较多的工作学习场景,我先以 Excel 型的表格数据举例,请你帮我构建一下下面这份 DataFrame
import pandas as pd
import numpy as np
data = np.arange(-12, 12).reshape((6, 4))
df = pd.DataFrame(
data,
index=list("abcdef"),
columns=list("ABCD"))
d
选Column
“Column”(列)通常指的是数据表格或数据框(DataFrame)中的一个垂直字段。每列包含了与某一特定变量或特征相关的数据
看到了上面这份数据后,我们发现,DataFrame 会分 Column 和 Row(index)。如果你搞机器学习,通常我们的 Column 是特征,Row 是数据样本, 在要对某个特征进行分析的时候,比如要做特征数值分布的分析,我们得把特征取出来吧
df["B"]
多个特征
print("numpy:\n", data[:, [2,1]])
print("\ndf:\n", df[["C", "B"]])
已经能选 column 了,那意味着肯定能将 Index(Row)的信息也一起考虑到数据筛选的工作当中了
loc
在 pandas
库中,.loc
是一个属性,用于通过标签(即行标签和列标签)进行数据选择和切片操作。它允许你根据行和列的标签来访问 DataFrame 或 Series 中的数据
如果你是刚从 面向 Excel 编程,或者也没怎么用过 Excel 中的函数的朋友,也可以轻松从 loc
函数中找到一丝丝熟悉的感觉。 因为这个 .loc
方法是更贴近我们人类直觉的,按照字面来选取数据块的方式。
在 Numpy 中选取数据一般都是按照在维度上的排序来定位的。 比如对于你刚刚创建的 Numpy 数据data
data[2:3, 1:3]
而在 DataFrame 中,同样是上述功能,你可以这么干
df.loc["c":"d", "B":"D"]
除了筛选一个片段,还可以像 Numpy 那样单个单个的选取
print("numpy:\n", data[[3,1], :])
print("\ndf:\n", df.loc[["d", "b"], :])
iloc
在 pandas
库中,.iloc
是一个用于基于位置(即整数位置)进行数据选择和切片的属性。它允许你通过行和列的位置(而不是标签)来访问 DataFrame 或 Series 中的数据
看完面向 Excel 编程,我们再来回到编程本身,用程序的思维去选取数据。这意味着什么?用最朴素的方法,也是意味着能更快找到数据位置,比如直接用位置信息来筛选。 Numpy 不就是这么干的吗?这时 .iloc
功能就派上用场了
同样还是上面那份数据,我们走一遍 .iloc
的流程
print("numpy:\n", data[2:3, 1:3])
print("\ndf:\n", df.iloc[2:3, 1:3])
简直和 Numpy 的模式一模一样,就是结果中多了一个 DataFrame 的标签信息
print("numpy:\n", data[[3,1], :])
print("\ndf:\n", df.iloc[[3, 1], :])
loc和iloc混搭
难免有时候,我们需要混搭 loc 和 iloc 的方式,比如我想要选取第 2 到第 4 位数据的 A C 两个特征,这时咋办? 想想 Pandas 这么牛逼,肯定有办法解决。的确,它解决的方法是采用索引转换的方式,比如我在 .loc
模式下,将序号索引转换成 .loc
的标签索引
row_labels = df.index[2:4]
print("row_labels:\n", row_labels)
print("\ndf:\n", df.loc[row_labels, ["A", "C"]])
再看看 Column 的 labels 怎么取
col_labels = df.columns[[0, 3]]
print("col_labels:\n", col_labels)
print("\ndf:\n", df.loc[row_labels, col_labels]
用 df.index
和 df.columns
来调取到全部的标签,然后在用像 Numpy index 索引的方式把这些标签给筛选出来,放到 .loc
里面用。 那反过来,我想要找 A B 两个特征的 前两个数据
col_index = df.columns.get_indexer(["A", "B"])
print("col_index:\n", col_index)
print("\ndf:\n", df.iloc[:2, col_index])
同理,df.index.get_indexer(["a", "b"])
也可以这样获取到 label 对应的 index 信息
条件过滤筛选
选在 A Column 中小于 0 的那些数据
df[df["A"] < 0
选在第一行数据不小于 -10 的数据,这里注意了你可以用两种方式,一种是 ~
来表示 非
什么什么,第二种是直接用 >=-10
来筛选
print("~:\n", df.loc[:, ~(df.iloc[0] < -10)])
print("\n>=:\n", df.loc[:, df.iloc[0] >= -10])
同上面类似的,我还能用或 |
来表示 or 的意思, &
表述 and
比如选在第一行数据不小于 -10 或小于 -11 的数据
i0 = df.iloc[0]
df.loc[:, ~(i0 < -10) | (i0 < -11)]
你可以用 .loc
或者 .iloc
来做过滤处理。然后用 .loc
来做筛选。为什么用 .iloc
来筛选呢,比如下面这样
df.iloc[:, ~(df.iloc[0] < -10)]
这样写居然会导致报错,为什么呢?你分析分析,把 ~(df.iloc[0] < -10)
这个筛选条件打印出来看看它是什么值。然后想一想 .iloc
能够接受的值是啥? 想清楚了,这个问题就迎刃而解了
在使用 pandas
的 .iloc
时,表达式 df.iloc[:, ~(df.iloc[0] < -10)]
的目的是通过布尔索引来选择列。让我们逐步解析这个表达式的含义:
-
df.iloc[0] < -10
:- 这个表达式会返回一个布尔 Series,表示 DataFrame 中第一行(即索引为 0 的行)的每个元素是否小于 -10。
- 例如,如果第一行的数据是
[5, -20, -15]
,则这个条件会生成一个布尔 Series[False, True, True]
。
-
~(df.iloc[0] < -10)
:~
是按位取反操作符。在布尔 Series 上应用~
操作符会将True
变为False
,将False
变为True
。- 例如,布尔 Series
[False, True, True]
经~
操作符处理后,会变为[True, False, False]
。
-
df.iloc[:, ~(df.iloc[0] < -10)]
:- 这个表达式通过
.iloc
选择 DataFrame 的列,其中列的选择是基于上一步生成的布尔 Series。 ~(df.iloc[0] < -10)
生成的布尔 Series[True, False, False]
表示要选择那些对应于True
的列,即 DataFrame 中的列 0(因为True
表示选择)会被保留下来,列 1 和 2 会被排除。
- 这个表达式通过
表达式 df.iloc[:, ~(df.iloc[0] < -10)]
选择了 DataFrame 中那些在第一行不满足小于 -10 条件的列。这是通过布尔索引和 .iloc
结合使用来实现的
Series和DataFrame类似
import pandas as pd
list_data = list(range(-4, 4))
s = pd.Series(
list_data,
index=list("abcdefgh"))
# 按标签筛选数据 .loc
print(s.loc[["a", "g", "c"]], "\n")
print(s.loc["c": "f"])
# 按 index 筛选数据 .iloc
print(s.iloc[[3, 1, 5]], "\n")
print(s.iloc[2: 4])
# iloc 和 loc 互相混用
print(s.iloc[s.index.get_indexer(["c", "d"])], "\n")
print(s.loc[s.index[[3,2]]])
# 按条件过滤筛选
print(s.loc[s < 3], "\n")
print(s.loc[(s < 0) & (s > -2)], "\n")
print(s.loc[(s < 0) | (s > 2)], "\n")
总结
Pandas 的数据筛选方法比 Numpy 丰富多了,介绍的篇幅也有点多,我日常用的最多的还是用条件来筛选,比如在处理机器学习的脏数据的时候,要用很多筛选逻辑。 有时甚至我会觉得 Numpy 的筛选方式不够多,专门把 Numpy 数据转换成 Pandas 数据,然后再用 Pandas 提供的丰富工具处理数据,再转回 Numpy。 相信你用熟了之后,数据处理将要简单很多