Pandas 01 - 数据类型和基本操作

233 阅读14分钟

数据类型和基本操作

Pandas 是非常著名的开源数据处理库,我们可以通过它完成对数据集进行快速读取、转换、过滤、分析等一系列操作。除此之外,Pandas 拥有强大的缺失数据处理与数据透视功能,可谓是数据预处理中的必备利器。

基础知识点如下:

  1. 数据类型
  2. 数据读取
  3. 数据选择
  4. 数据删减
  5. 数据填充

一、引言

Pandas 是非常著名的开源数据处理库,其基于 NumPy 开发,该工具是 Scipy 生态中为了解决数据分析任务而设计。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的函数和方法。特有的数据结构是 Pandas 的优势和核心。简单来讲,我们可以将任意格式的数据转换为 Pandas 的数据类型,并使用 Pandas 提供的一系列方法进行转换、操作,最终得到我们期望的结果。

二、数据类型

Pandas 的数据类型主要有以下几种,它们分别是:

  1. Series(一维数组)
  2. DataFrame(二维数组)
  3. Panel(三维数组)
  4. Panel4D(四维数组)
  5. PanelND(更多维数组)。

其中 Series 和 DataFrame 应用的最为广泛,几乎占据了使用频率 90% 以上。

import numpy as np
import pandas as pd
%matplotlib inline

1. Series

Series 是 Pandas 中最基本的一维数组形式。其可以储存整数、浮点数、字符串(字符串相当于二维数组)等类型的数据。Series 基本结构如下:

pd.Series(data=None, index=None)
F:\Temp1/ipykernel_10284/3044099724.py:1: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  pd.Series(data=None, index=None)





Series([], dtype: float64)

其中,data可以是python里面的字典dict,或者是numpy中的narray阵列等。index是数据索引,索引是Pandas数据结构中的一大特性,它主要的功能是帮助我们更快速地定位数据data。

下面,我们基于 Python 字典新建一个示例 Series

dict = {'a':'chengyichen', 'b':'qinlang'}
s = pd.Series(data = dict)
s
a    chengyichen
b        qinlang
dtype: object
dict = {'a':123, 'b':452}
s = pd.Series(data = dict)
s
a    123
b    452
dtype: int64

可以看出Series里面数字默认类型为 int64,而字符串默认为对象

可以利用type查看Series变量类型

type(s)
pandas.core.series.Series

由于 Pandas 基于 NumPy 开发。那么 NumPy 的数据类型 ndarray 多维数组自然就可以转换为 Pandas 中的数据。而 Series 则可以基于 NumPy 中的一维数据转换。

na = np.ones((3,3), np.uint8)
# 注意:Series作为一维数组,只能接收一维阵列!
s = pd.Series(data = na.flatten())
s
0    1
1    1
2    1
3    1
4    1
5    1
6    1
7    1
8    1
dtype: uint8

可以看出,数据类型和data保持一致且索引值从0开始

2. DataFrame

DataFrame 是 Pandas 中最为常见、最重要且使用频率最高的数据结构。DataFrame 和平常的电子表格或 SQL 表结构相似。你可以把 DataFrame 看成是 Series 的扩展类型,它仿佛是由多个 Series 拼合而成。它和 Series 的直观区别在于,数据不但具有行索引,且具有列索引。

download.png

DataFrame 基本结构如下:

pd.DataFrame(data=None, index=None, columns=None)
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }

区别于 Series,其增加了 columns 列索引。DataFrame 可以由以下多个类型的数据构建:

  1. 一维数组、列表、字典或者 Series 字典。
  2. 二维或者结构化的 numpy.ndarray。
  3. 一个 Series 或者另一个 DataFrame。 例如,我们首先使用一个由 Series 组成的字典来构建 DataFrame。
s1 = pd.Series(data={'a':1, 'b':0})
s2 = pd.Series(data={'c':0, 'd':1})

d = pd.DataFrame(data={'one':s1, 'two':s2})
d
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
one two
a 1.0 NaN
b 0.0 NaN
c NaN 0.0
d NaN 1.0

若是使用字典进行初始化,那么行序列就是Series字典索引,列序列就是输入的字典序列

df = pd.DataFrame({'one': pd.Series([1, 2, 3]),
                   'two': pd.Series([4, 5, 6])})
df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
one two
0 1 4
1 2 5
2 3 6

当Series不指定索引时,DataFrame 的索引同样是从 0 开始。我们也可以直接通过一个列表构成的字典来生成 DataFrame。

df = pd.DataFrame({'one': [1, 2, 3],
                   'two': [4, 5, 6]})
df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
one two
0 1 4
1 2 5
2 3 6

或者反过来,由带字典的列表生成 DataFrame。

df = pd.DataFrame([{'one': 1, 'two': 4},
                   {'one': 2, 'two': 5},
                   {'one': 3, 'two': 6}])
df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
one two
0 1 4
1 2 5
2 3 6

NumPy 的多维数组非常常用,同样可以基于二维数值来构建一个 DataFrame。

d = pd.DataFrame(np.random.randint(5, size=(2, 4)))
d
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
0 1 2 3
0 0 2 2 1
1 2 4 4 2

至此,你应该已经清楚了 Pandas 常用的 Series 和 DataFrame 数据类型。Series 实际上可以被初略看出是只有 1 列数据的 DataFrame。当然,这个说法不严谨,二者的核心区别仍然是 Series 没有列索引。你可以观察如下所示由 NumPy 一维随机数组生成的 Series 和 DataFrame。

# size的输入类型要为元组
pd.Series(np.random.randint(5, size=(5,)))
0    1
1    2
2    3
3    0
4    3
dtype: int32
pd.DataFrame(np.random.randint(5, size=(5,)))
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
0
0 4
1 4
2 1
3 4
4 2

关于 Pandas 中的 Panel 等数据类型我们就不再介绍。首先是这些数据类型用的很少,其次就算你用到了,也可以通过从 DataFrame 等学到的技巧进行迁移应用,万变不离其宗。

三、数据读取

我们想要使用 Pandas 来分析数据,那么首先需要读取数据。大多数情况下,数据都来源于外部的数据文件或者数据库。Pandas 提供了一系列的方法来读取外部数据,非常全面。下面,我们以最常用的 CSV 数据文件为例进行介绍。

读取数据 CSV 文件的方法是 pandas.read_csv(),你可以直接传入一个相对路径,或者是网络 URL。

df = pd.read_csv("https://labfile.oss.aliyuncs.com/courses/906/los_census.csv")
df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
0 91371 1 73.5 0 1 1 1.00
1 90001 57110 26.6 28468 28642 12971 4.40
2 90002 51223 25.5 24876 26347 11731 4.36
3 90003 66266 26.3 32631 33635 15642 4.22
4 90004 62180 34.8 31302 30878 22547 2.73
... ... ... ... ... ... ... ...
314 93552 38158 28.4 18711 19447 9690 3.93
315 93553 2138 43.3 1121 1017 816 2.62
316 93560 18910 32.4 9491 9419 6469 2.92
317 93563 388 44.5 263 125 103 2.53
318 93591 7285 30.9 3653 3632 1982 3.67

319 rows × 7 columns

由于 CSV 存储时是一个二维的表格,那么 Pandas 会自动将其读取为 DataFrame 类型

现在你应该就明白了,DataFrame 是 Pandas 构成的核心。一切的数据,无论是外部读取还是自行生成,我们都需要先将其转换为 Pandas 的 DataFrame 或者 Series 数据类型。实际上,大多数情况下,这一切都是设计好的,无需执行额外的转换工作。

pd.read_ 前缀开始的方法还可以读取各式各样的数据文件,且支持连接数据库。 这里,我们不再依次赘述,你可以阅读 官方文档相应章节pandas.pydata.org/pandas-docs… 熟悉这些方法以及搞清楚这些方法包含的参数。

  • 为什么要将读入的数据转化为 Series 或者 DataFrame 结构?

因为 Pandas 针对数据操作的全部方法都是基于 Pandas 支持的数据结构设计的。也就是说,只有 Series 或者 DataFrame 才能使用 Pandas 提供的方法和函数进行处理。所以,学习真正数据处理方法之前,我们需要将数据转换生成为 Series 或 DataFrame 类型。到时候处理完毕导出的时候应该可以转换为其他类型

四、基本操作

通过上面的内容,我们已经知道一个 DataFrame 结构大致由 3 部分组成,它们分别是列名称、索引和数据。

download.png

接下来,我们就学习针对 DataFrame 的基本操作。本次课程中,我们不会刻意强调 Series,因为你在 DataFrame 上学习的大多数方法和技巧都适用于对 Series 进行处理,二者同根同源。

上面,我们已经读取了一个外部数据,这是洛杉矶的人口普查数据。有些时候,我们读取的文件很大。如果全部输出预览这些文件,既不美观,又很耗时。还好,Pandas 提供了 head() 和 tail() 方法,它可以帮助我们只预览一小块数据。

df.head()  # 默认显示前 5 条
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
0 91371 1 73.5 0 1 1 1.00
1 90001 57110 26.6 28468 28642 12971 4.40
2 90002 51223 25.5 24876 26347 11731 4.36
3 90003 66266 26.3 32631 33635 15642 4.22
4 90004 62180 34.8 31302 30878 22547 2.73
df.tail(7)  # 指定显示后 7 条
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
312 93550 74929 27.5 36414 38515 20864 3.58
313 93551 50798 37.0 25056 25742 15963 3.18
314 93552 38158 28.4 18711 19447 9690 3.93
315 93553 2138 43.3 1121 1017 816 2.62
316 93560 18910 32.4 9491 9419 6469 2.92
317 93563 388 44.5 263 125 103 2.53
318 93591 7285 30.9 3653 3632 1982 3.67

Pandas 还提供了统计和描述性方法,方便你从宏观的角度去了解数据集。describe() 相当于对数据集进行概览,会输出该数据集每一列数据的计数、最大值、最小值等。

df.describe()
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
count 319.000000 319.000000 319.000000 319.000000 319.000000 319.000000 319.000000
mean 91000.673981 33241.341693 36.527586 16391.564263 16849.777429 10964.570533 2.828119
std 908.360203 21644.417455 8.692999 10747.495566 10934.986468 6270.646400 0.835658
min 90001.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 90243.500000 19318.500000 32.400000 9763.500000 9633.500000 6765.500000 2.435000
50% 90807.000000 31481.000000 37.100000 15283.000000 16202.000000 10968.000000 2.830000
75% 91417.000000 44978.000000 41.000000 22219.500000 22690.500000 14889.500000 3.320000
max 93591.000000 105549.000000 74.000000 52794.000000 53185.000000 31087.000000 4.670000

Pandas 基于 NumPy 开发,所以任何时候你都可以通过 .values 将 DataFrame 转换为 NumPy 数组。

# 但是丢失了列索引
df.values
array([[9.1371e+04, 1.0000e+00, 7.3500e+01, ..., 1.0000e+00, 1.0000e+00,        1.0000e+00],
       [9.0001e+04, 5.7110e+04, 2.6600e+01, ..., 2.8642e+04, 1.2971e+04,        4.4000e+00],
       [9.0002e+04, 5.1223e+04, 2.5500e+01, ..., 2.6347e+04, 1.1731e+04,        4.3600e+00],
       ...,
       [9.3560e+04, 1.8910e+04, 3.2400e+01, ..., 9.4190e+03, 6.4690e+03,        2.9200e+00],
       [9.3563e+04, 3.8800e+02, 4.4500e+01, ..., 1.2500e+02, 1.0300e+02,        2.5300e+00],
       [9.3591e+04, 7.2850e+03, 3.0900e+01, ..., 3.6320e+03, 1.9820e+03,        3.6700e+00]])

这也就说明了,你可以同时使用 Pandas 和 NumPy 提供的 API 对同一数据进行操作,并在二者之间进行随意转换。这就是一个非常灵活的工具生态圈。

除了 .values,DataFrame 支持的常见属性可以通过 官方文档相应章节 pandas.pydata.org/pandas-docs… 查看。其中常用的有:

df.index  # 查看索引
RangeIndex(start=0, stop=319, step=1)
df.columns  # 查看列名
Index(['Zip Code', 'Total Population', 'Median Age', 'Total Males',       'Total Females', 'Total Households', 'Average Household Size'],
      dtype='object')
df.shape  # 查看形状
(319, 7)

五、数据选择

在数据预处理过程中,我们往往会对数据集进行切分,只将需要的某些行、列,或者数据块保留下来,输出到下一个流程中去。这也就是所谓的数据选择,或者数据索引。

由于 Pandas 的数据结构中存在索引、标签,所以我们可以通过多轴索引完成对数据的选择。

1. 基于索引数字选择

当我们新建一个 DataFrame 之后,如果未自己指定行索引或者列对应的标签,那么 Pandas 会默认从 0 开始以数字的形式作为行索引,并以数据集的第一行作为列对应的标签。其实,这里的「列」也有数字索引,默认也是从 0 开始,只是未显示出来。

所以,我们首先可以基于数字索引对数据集进行选择。这里用到的 Pandas 中的 .iloc 方法。该方法可以接受的类型有:

  1. 整数。例如:5
  2. 整数构成的列表或数组。例如:[1, 2, 3]
  3. 布尔数组。
  4. 可返回索引值的函数或参数。

首先,我们可以选择前 3 行数据。这和 Python 或者 NumPy 里面的切片很相似。

df.iloc[:3]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
0 91371 1 73.5 0 1 1 1.00
1 90001 57110 26.6 28468 28642 12971 4.40
2 90002 51223 25.5 24876 26347 11731 4.36

我们还可以选择特定的一行。

df.iloc[5]
Zip Code                  90005.0
Total Population          37681.0
Median Age                   33.9
Total Males               19299.0
Total Females             18382.0
Total Households          15044.0
Average Household Size        2.5
Name: 5, dtype: float64

那么选择多行,是不是 df.iloc[1, 3, 5] 这样呢?

答案是错误的。df.iloc[] 的 [[行],[列]] 里面可以同时接受行和列的位置,如果你直接键入 df.iloc[1, 3, 5] 就会报错

## 如果你想要选择 2,4,6 行,从0开始,可以这样做。
df.iloc[[1, 3, 5]]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
1 90001 57110 26.6 28468 28642 12971 4.40
3 90003 66266 26.3 32631 33635 15642 4.22
5 90005 37681 33.9 19299 18382 15044 2.50

选择行学会以后,选择列就应该能想到怎么办了。例如,我们要选择第 2-4 列。

df.iloc[:, 1:4]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Total Population Median Age Total Males
0 1 73.5 0
1 57110 26.6 28468
2 51223 25.5 24876
3 66266 26.3 32631
4 62180 34.8 31302
... ... ... ...
314 38158 28.4 18711
315 2138 43.3 1121
316 18910 32.4 9491
317 388 44.5 263
318 7285 30.9 3653

319 rows × 3 columns

# 倒数第一、二列
df.iloc[:, [-2,-1]]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Total Households Average Household Size
0 1 1.00
1 12971 4.40
2 11731 4.36
3 15642 4.22
4 22547 2.73
... ... ...
314 9690 3.93
315 816 2.62
316 6469 2.92
317 103 2.53
318 1982 3.67

319 rows × 2 columns

这里选择 2-4 列,输入的却是 1:4。这和 Python 或者 NumPy 里面的切片操作非常相似。既然我们能定位行和列,那么只需要组合起来,我们就可以选择数据集中的任何数据了。

2. 基于标签名称选择

除了根据数字索引选择,还可以直接根据标签对应的名称选择。这里用到的方法和上面的 iloc 很相似,少了个 i 为 df.loc[]。

df.loc[] 可以接受的类型有:

  1. 单个标签。例如:2 或 'a',这里的 2 指的是标签而不是索引位置(从0开始)。
  2. 列表或数组包含的标签。例如:['A', 'B', 'C']。
  3. 切片对象。例如:'A':'E',注意这里和上面切片的不同之处,首尾都包含在内。
  4. 布尔数组。
  5. 可返回标签的函数或参数。

下面,我们来演示 df.loc[] 的用法。先选择前 3 行:

df.loc[0:2]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Zip Code Total Population Median Age Total Males Total Females Total Households Average Household Size
0 91371 1 73.5 0 1 1 1.00
1 90001 57110 26.6 28468 28642 12971 4.40
2 90002 51223 25.5 24876 26347 11731 4.36

然后,选择 2-4 列:

df.loc[:, 'Total Population':'Total Males']
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Total Population Median Age Total Males
0 1 73.5 0
1 57110 26.6 28468
2 51223 25.5 24876
3 66266 26.3 32631
4 62180 34.8 31302
... ... ... ...
314 38158 28.4 18711
315 2138 43.3 1121
316 18910 32.4 9491
317 388 44.5 263
318 7285 30.9 3653

319 rows × 3 columns

最后,选择 1,3 行和 Median Age 后面的列:

df.loc[[0, 2], 'Median Age':]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
Median Age Total Males Total Females Total Households Average Household Size
0 73.5 0 1 1 1.00
2 25.5 24876 26347 11731 4.36