介绍与安装

一个优秀的大模型开发工程师,首先一定是一名优秀的数据分析师。而Pandas作为Python数据分析的核心库之一,在数据处理上有着无可比拟的优势,其开发迅速,代码灵活,无论是在数据分析领域、还是大数据开发场景中都具有显著的优势。

Pandas是一个第三方包,如果还没有安装,可以通过以下命令安装:

pip install pandas

使用Pandas功能前需要先导入:

import pandas as pd

Pandas的数据结构

DataFrame

在 Pandas 库中,最主要使用的数据结构是 DataFrame,它为数据处理和分析提供了强大且灵活的工具。DataFrame 是一个二维的、表格型的数据结构,非常类似于 SQL 数据库表或 Excel 电子表格。DataFrame 可以存储不同类型的列,如整数、浮点数、字符串等。

DataFrame是Pandas库中最为常用的数据结构,需要数据清洗/分析时我们经常创建它来读取和操作数据。

创建简单的DataFrame

创建DataFrame的方式其实有很多,实际开发中我们往往是从数据库中获取数据,或者是读取 csv,excel,json等文件格式中的数据这些我们后面会提到,此处为了演示数据结构,我们先手动创建DataFrame对象。

1.字典加列表创建DataFrame,自增索引

import pandas as pd

# 创建一个 DataFrame
data = {'Name': ['小明', '小红', '王刚'],
        'Age': [20, 21, 19]}
df = pd.DataFrame(data)

# 显示 DataFrame
print(df)

输出为:

   Name    Age
0  小明    20
1  小红    21
2  王刚    19

可以看到,我们用字典加列表的形式创建了一个三行两列名为df的DataFrame对象,我们传进来的是一个字典,字典的key成了df的列名,字典的value成了df的值,且旁边有从0开始的行索引。

DataFrame 的列代表数据的特征或变量。每列在 DataFrame 中有一个列名(比如示例中的 Name 和 Age),这些列名组成了 DataFrame 的列索引。不同列的数据可以有不同的数据类型,但每一列中的数据应该具有相同的数据类型,比如:整数、浮点数、字符串等。列可以通过列名来访问和操作。程序中可以动态地插入、删除修改列。

DataFrame 的行表示数据记录,每一行包含了一组相关的数据。行可以通过行索引(标签)来访问和操作。我们在创建DataFrame的时候是可以指定行索引的,如果没有指定,程序也会自动生成的从 0 开始的行索引,比如实例中输出的0,1,2。行也同样可动态地插入、删除修改列。索引是一个不可变序列,用于标识 DataFrame 的行。很多数据操作,比如数据对齐、合并、连接和分组等都会使用到索引。

2.列表加元组创建DataFrame,自定义索引

import pandas as pd

df2_data = [
    ('2021-08-21', 25, 81),
    ('2021-08-22', 26, 50),
    ('2021-08-23', 27, 56)
]

df2 = pd.DataFrame(
    data=df2_data, 
    columns=['日期', '温度', '湿度'],
    index = ['row_1','row_2','row_3'] # 手动指定索引
)
print(df2)

输出为:

            日期       温度  湿度
row_1    2021-08-21    25    81
row_2    2021-08-22    26    50
row_3    2021-08-23    27    56

可以看到,我们用列表加元组的形式创建了一个三行三列名为df2的DataFrame对象,我们将索引名和数值分开设置,用列表嵌套元组的形式来给DataFrame的数据赋值,每一个元组对应着DataFrame中的一行数据,同时通过columnsindex两个关键词参数手动设置了列索引行索引

3.使用Numpy创建DataFrame,自定义索引

实际开发中我们可能需要创建一个比较大的DataFrame来验证自己的想法,但是手动一个个构造数据太慢了,这时候我们可以通过Numpy来构造指定大小的随机数组,再转成DataFrame。

import pandas as pd
import numpy as np

# 构造一个10行5列的数组,并且每个数据是[40,100)的整数
score = np.random.randint(40, 100, (10, 5))
# 构造列索引序列
subjects = ["语文", "数学", "英语", "政治", "体育"]

# 构造行索引序列 score.shape[0]是取数组的第一个维度的大小,即数组的行数
stu = ['同学' + str(i) for i in range(score.shape[0])]

# 添加索引
df3 = pd.DataFrame(score, columns=subjects, index=stu)
print(df3)

输出为:

       语文  数学  英语  政治  体育
同学0   72   64    83   41    86
同学1   96   46    63   69    73
同学2   48   69    62   61    76
同学3   91   93    80   71    48
同学4   80   79    59   43    89
同学5   59   89    56   52    91
同学6   67   92    88   82    57
同学7   95   69    83   61    63
同学8   89   80    82   99    51
同学9   63   76    55   66    96

我们用数组来填充数据,同时设置了行索引列索引以增加可读性,这样是不是清晰多了?

索引

默认情况下,DataFrame 采用行号作为行索引,但行索引也可以是日期、字符串或其他数据。比如在上面的程序,我们在创建 DataFrame 对象时,指定了 index 参数,产生的对象就会使用 index 的数据作为索引。

我们也可以使用 set_index方法把DataFrame某一列或某几列数据设置为索引。比如 df.set_index('Name', inplace=True) 将会把 Name 列设置为索引,数据会变成:

       Age
Name     
小明    20
小红    21
王刚    19

查看DataFrame的信息

下面是一些常用的查看 DataFrame 的信息的方法,采用哪种方法取决于想要获取的信息类型。

  1. 查看DataFrame的形状:使用.shape属性可以快速查看DataFrame的行数和列数。

    df.shape
    
  2. 查看列数据类型:使用.dtypes属性可以查看DataFrame中每列的数据类型。

    df.dtypes
    
  3. 查看前几行:使用.head(n)方法可以查看DataFrame的前n行数据,默认是前5行。

    df.head()
    
  4. 查看概览信息:使用.info()方法可以查看DataFrame的概览信息,包括每列的数据类型、非空值数量等。

    df.info()
    
  5. 描述性统计:使用.describe()方法可以生成每列的描述性统计摘要,包括均值、标准差、最小值、最大值等。

    df.describe()

Series

Pandas 也支持其它一些数据结构,但是使用频率远低于 DataFrame。所以其它数据类型就不多详述了,只用 Series 作为示例,简单说明一下:

Series 是一维的标签化数组,可以容纳任何数据类型(整数、字符串、浮点数、Python 对象等)。每个 Series 对象都有一个索引,可以通过索引来访问数组中的单个数据项。Series 中的所有数据项必须属于同一数据类型。一旦创建,Series 的长度是固定的,不能改变。但是,可以改变里面的数据。Series 可以有一个轴标签,可以使用标签来访问数据。

import pandas as pd
import numpy as np

# 构造一个10行5列的数组,并且每个数据是[40,100)的整数
score = np.random.randint(40, 100, (10, 5))
# 构造列索引序列
subjects = ["语文", "数学", "英语", "政治", "体育"]

# 构造行索引序列 
stu = ['同学' + str(i) for i in range(score.shape[0])]

# 添加索引
df3 = pd.DataFrame(score, columns=subjects, index=stu)
print(df3)

print('--------------')

s1 = df3['语文']
print(type(s1),s1['同学0'])  

s2 = df3.loc['同学3']
print(type(s2),s2['英语'])

输出为:

     语文  数学  英语  政治  体育
同学0  93  48    57   43    73
同学1  96  94    79   97    77
同学2  46  41    76   72    53
同学3  77  67    59   85    62
同学4  72  89    57   53    48
同学5  59  99    48   91    95
同学6  65  93    86   83    74
同学7  46  80    53   62    49
同学8  46  91    53   95    57
同学9  69  61    77   64    82
--------------
<class 'pandas.core.series.Series'> 93
<class 'pandas.core.series.Series'> 59

从上述代码可以看出,无论从DataFrame中取出某一行或者某一列,都会变成一维的Series。因为是一维,所以Series并没有行索引和列索引的概念,只有一个索引。如果是通过DataFrame的某一行创建,那么DataFrame的列索引就会成为Series的索引,如果是通过DataFrame的某一列创建,那么DataFrame的行索引就会成为Series的索引。

通过Series的索引就可以访问到Series内部的数据了。

创建简单的Series

1.通过列表创建Series对象

import pandas as pd

# 使用默认自增索引
s2 = pd.Series([1, 2, 3])
print(s2)
# 自定义索引
s3 = pd.Series([1, 2, 3], index=['A', 'B', 'C'])
print(s3)

输出为:

结果为:
0    1
1    2
2    3
dtype: int64
A    1
B    2
C    3
dtype: int64

2.通过元组创建Series对象

import pandas as pd

tst = (1,2,3,4,5,6)
pd.Series(tst)

3.通过字典创建Series对象

import pandas as pd

#使用字典:
dst = {'A':1,'B':2,'C':3,'D':4,'E':5,'F':6}
pd.Series(dst)

4.通过Numpy创建Series对象

import pandas as pd

s = pd.Series(np.arange(10))
print(s)

np.arange(10)生成了一个从0开始,长度为10的一维数组,输出为:

0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int32

读取文件和数据库

Pandas 提供了多种功能强大的函数来读取不同类型的文件,使得数据导入变得简单高效。这些函数可以处理各种常见的数据格式,如 CSV、Excel、JSON、和 SQL 数据库等。

读写 CSV, TSV 文件

CSV(逗号分隔值) 和 TSV(制表符分隔值)文件是最常见的文本文件格式,用于存储表格数据。这些文件中,数据按照行列保存,CSV 格式的文件,每列数据间用逗号分隔,TSV 格式文件中,每列数据用制表符分隔。我们可以使用 pd.read_csv() 函数读取这两种文件,只需指定适当的分隔符即可。

pd.read_csv() 函数的参数包括:

  • filepath_or_buffer: 文件的路径或类似文件的对象。

  • sep: 字段分隔符,默认为英文逗号。

  • header: 用作列名的行号,默认为 0 (第一行)。

  • index_col: 用作行索引的列编号或列名。

  • usecols: 返回的列的子集,可以用来选择自己需要的列。

  • dtype: 列的数据类型。

如果我们的数据标准的话(数据之间用英文逗号隔开,第一行为列名,使用默认的行索引,返回文件里的所有列),那么我们只需要一个文件路径就可以读取文件了。假设我们有一个名为 data.tsv 的文件,内容如下:

Name,Age,City
杜其演,24,上海
宋外麦,19,北京

读取这个文件的代码将是:

df = pd.read_csv('data.tsv')
print(df)

因为这个数据文件有表头,也就是 Name Age City,所以 header 参数采用默认值 0。如果文件中的数据没有表头,使用 header=None 作为参数即可。也可以再另外加一个参数 names=['Name', 'Age', 'City'],这样就指定了读取的数据的列名。

上面程序的输出是:

    Name  Age    City
0   杜其演   24    上海
1   宋外麦   19    北京

把表格数据写入文本文件,与读取非常类似,使用 pd.to_csv() 函数。这个函数的参数也与 pd.read_csv() 函数的参数类似:

  • path_or_buf:文件路径或一个类似文件的对象。如果未指定路径,结果将返回为字符串。

  • sep:字段间的分隔符,默认为逗号 ','。

  • index:是否将行索引写入文件,默认为 True。通常设置为 False,除非你需要行索引作为数据的一部分。

  • header:是否写入列名(表头),默认为 True。

  • columns:指定要写入文件的列名列表。

  • encoding:指定文件的编码格式,默认是 'utf-8'。

假设把上面读取的数据在存入一个 CSV 文件中,调用下面这行程序即可:

df.to_csv('my_data.csv', index=False)

读写Excel文件

使用 Pandas 读写 Excel 文件非常简单。但是 Pandas 本身并不能解析 Excel,它也是调用其它库来读写 Excel 的。所以,我们需要安装以下这些库:

# 用于处理 .xlsx 文件
pip install pandas openpyxl
# 用于处理 .xls 文件
pip install pandas xlrd

之后,就可以使用 to_excel() 和 read_excel() 函数来写入和读取 .xls和 .xlsx 格式的文件。这两个函数的参数与 to_csv(), read_csv() 函数非常类似。主要区别在于多了一个参数 sheet_name,用于指定工作表的名称,默认为 "Sheet1"。下面是一个简单示例:

import pandas as pd

# 创建一个示例 DataFrame
data = {'Name': ['姜米调', '高丽黛', '钱泰少'],
        'Age': [25, 30, 35],
        'City': ['上海', '北京', '西安']}
df = pd.DataFrame(data)

# 将 DataFrame 写入 Excel 文件
df.to_excel('my_data.xlsx', index=False)

# 读取名为 'Sheet2' 的工作表
df_2 = pd.read_excel('file.xlsx', sheet_name='Sheet2')

# 显示 DataFrame
print(df_2)

上面的程序把 DataFrame 数据写入了一个 Excel 文件,之后又把数据从文件里读入到了一个新的 DataFrame

读写SQL数据库

使用 Pandas ,读写数据库也需要先安装所需的模块:

pip install sqlalchemy pymysql pyhive

Pandas 使用 read_sql、read_sql_query 或 read_sql_table 函数来从数据库读取数据。首先,需要创建一个 SQLAlchemy 引擎对象,这个对象代表了数据库连接。

from sqlalchemy import create_engine

# 创建数据库引擎
# 示例为 SQLite 数据库,其他数据库的连接字符串格式可能不同
engine = create_engine('sqlite:///mydatabase.db')

# Hive 数据库代码类似: engine = create_engine('hive://hostname:port/database')

接下来使用 read_sql 函数或其变体从数据库中读取数据:

import pandas as pd

# 使用SQL查询
df = pd.read_sql("SELECT * FROM my_table", con=engine)

# 或者直接从表格读取
df = pd.read_sql_table("my_table", con=engine)

使用 to_sql 方法,可以将 DataFrame 的内容写入数据库表中:

df.to_sql("my_table", con=engine, if_exists='replace', index=False)

这里 if_exists 参数控制如果表已经存在时的行为,它有三个值可以填:

  • 值为 'fail',表示如果表存在,抛出错误

  • 值为'replace' ,表示如果表存在,替换原有表

  • 值为'append' 表示如果表存在,将数据追加到表中

面对表不存在的情况,这三个值都会自动创建表

数据库的 URL 格式依赖于所使用的数据库类型。通常它包含用户名、密码、主机、端口和数据库名。将 DataFrame 写入数据库时,要确保 DataFrame 的结构与目标数据库表兼容。

读取DataFrame中的数据

读取列

使用列名可以选取一个或多个列的数据,比如:

# 通过列名选择单列,返回的是series:
series = df['Name'] 

# 选择多列,返回的是dataframe:
new_df = df[['Name', 'Age']]

读取行

读取行数据有两种方式:通过索引(loc)或通过行号(iloc):

import pandas as pd

# 创建一个示例 DataFrame
data = {'Name': ['刘备', '关羽', '张飞'],
        'Age': [40, 35, 30],
        'City': ['涿郡', '解县', '涿郡']}
df = pd.DataFrame(data)

# 把 City 列设置为索引
df.set_index('City', inplace=True)

# 通过行号选择行
print(df.iloc[0])      # 输出第一行数据
print(df.iloc[0, 2])   # 输出第一和第三行数据

# 通过索引选择行
print(df.loc['涿郡'])   # 输出以涿郡为索引的两行数据

需要注意的是,Pandas里行索引列索引的名字都是可以重复的,以上面代码为例,最终输出的是行索引相同的多行数据,在实际开发中我们不建议设置相同的行索引或者列索引,避免引起混淆和数据处理错误。

切片

DataFrame 切片功能与列表的切片是非常类似的。通常表格中的数据行多列少,切片主要用于选择行。比如:

import pandas as pd

# 创建一个示例 DataFrame
data = {'Name': ['刘备', '关羽', '张飞'],
        'Age': [40, 35, 30],
        'City': ['涿郡', '解县', '涿郡']}
df = pd.DataFrame(data)

# 使用行号进行行切片
print(df[:2])           # 切片前两行

# 使用行列号进行切片
print(df.iloc[:2, 1:])  # 切片前两行、后两列

条件选择

我们还可以使用条件表达式选择满足特定条件的行:

import pandas as pd

# 创建一个示例 DataFrame
data = {'Name': ['赵云', '黄忠', '马超'],
        'Age': [25, 60, 30],
        'City': ['常山', '南阳', '茂陵']}
df = pd.DataFrame(data)

print(df[df['Age'] > 25])          # 选择年龄大于25岁的行
print(df[df['City'] == '南阳'])     # 选择城市为南阳的行

print(df[df['Age'] > 25] & df['City'] == '南阳'])  # 选择年龄大于25岁并且城市为南阳的行

print(df[df['Age'] > 25]['Name'])  # 结合行条件和列选择

访问单个数据

我们可以使用 at 通过索引和列名或者使用 iat 通过行列号来访问单个元素数据:

import pandas as pd

data = {'Name': ['孙权', '刘表', '曹操'],
        'Born': ['182', '142', '155'],
        'Died': ['252', '208', '220']}
df = pd.DataFrame(data)

# 把 Name 列设置为索引
df.set_index('Name', inplace=True)

# 使用at访问元素
sunquan_born = df.at['孙权', 'Born']
print("孙权出生年份:", sunquan_born)

# 使用iat访问元素
liubiao_died = df.iat[1, 1]
print("刘表死亡年份:", liubiao_died)

迭代

在程序中,多数时候不会只读取 DataFrame 中的某个数据,而是需要使用循环等方式,迭代处理 DataFrame 中的每一个数据。

直接迭代列

直接迭代 DataFrame 实际上是迭代其列名,比如:

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

for col in df:
    print(f"Column: {col}, Data: {df[col].tolist()}")
    
# 输出:
# Column: A, Data: [1, 2, 3]
# Column: B, Data: [4, 5, 6]

不过,实际程序中,更多的时候会需要逐行处理数据。行迭代有以下几种主要方法。

使用 iterrows 迭代行

iterrows() 是一个生成器,它为 DataFrame 中的每一行产生一个元组(索引,系列)。这是最常用的按行迭代方法。

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

for index, row in df.iterrows():
    print(f"Index: {index}, A: {row['A']}, B: {row['B']}")
    
# 输出:
# Index: 0, A: 1, B: 4
# Index: 1, A: 2, B: 5
# Index: 2, A: 3, B: 6    

这种方法在处理大型 DataFrame 时可能不太高效,因为每一行都返回一个 Series 对象。

使用 itertuples 迭代行

itertuples()是另一个生成器,它为DataFrame中的每一行产生一个命名元组。这个方法通常比iterrows()更快。

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

for row in df.itertuples():
    print(f"Index: {row.Index}, A: {row.A}, B: {row.B}")
    
# 输出结果与上面的示例完全相同

itertuples() 比 iterrows() 快,但要注意,行索引在 itertuples() 中是作为属性 Index 返回的。

使用 apply() 函数

我们也可以使用高阶函数对 DataFrame 中的每个数据进行处理。apply()函数虽然不是专门用于迭代的,但可以用来对 DataFrame 中的每一行或列执行操作。

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
def process(row):
    print(f"A: {row['A']}, B: {row['B']}")

df.apply(process, axis=1)

# 输出:
# A: 1, B: 4
# A: 2, B: 5
# A: 3, B: 6    

增删数据

这里只提到了增加和删除数据,为什么不提修改?聪明的读者应该反应过来了,修改其实就是先定位到具体目标,再重新赋值,而定位的方法就是上述提到的各种读取数据的方式了。

添加列

使用新的列名就可以为数据添加新的列:

import pandas as pd

# 创建一个示例 DataFrame
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

# 添加新列
df['C'] = [7, 8, 9]

print(df)

输出为:

   A  B  C
0  1  4  7
1  2  5  8
2  3  6  9

在上面的章节里我们学到了定位到DataFrame的某一列,可以用df[列名]的形式,也可以用df.列名,列名此时是DataFrame的属性,但是要注意的是,当我们添加新列的时候,只能用df[列名]的形式

添加行

早期的 Pandas 通常使用 append 方法添加数据,但这个方法已经被淘汰了。现在推荐的方法是使用concat函数向 DataFrame 添加新行。新行的数据应该以字典形式提供,其键与 DataFrame 的列名对应:

# 创建一个包含多个新行的 DataFrame
new_rows = pd.DataFrame({'A': [5, 6], 'B': [8, 9], 'C': [11, 12]})

# 调用 concat 合并原 DataFrame 和新 DataFrame
df = pd.concat([df, new_rows], ignore_index=True)

删除数据

删除行列

pandas 使用 df.drop() 函数删除行或列。 这个函数最常用的参数是 labels 和 axis。labels 表示一个或多个删除的行或列的标签。axis 指定要删除的是行还是列: axis=0 (或 axis='index') 表示删除行;axis=1 (或 axis='columns') 表示删除列。

作为演示,我们首先创建一个 DataFrame:

import pandas as pd

# 创建一个示例 DataFrame
data = {
    'A': [1, 2, 3, 4, 4],
    'B': [5, 6, None, 8, 8],
    'C': [10, 11, 12, 13, 13]
}
df = pd.DataFrame(data)

print(df)

输出为:

   A    B   C
0  1  5.0  10
1  2  6.0  11
2  3  NaN  12
3  4  8.0  13
4  4  8.0  13

运行下面的程序,打印的是删除了 'B' 列的数据:

print(df.drop(['B'], axis=1))

输出为:

   A   C
0  1  10
1  2  11
2  3  12
3  4  13
4  4  13

运行下面的程序,删除第 0 行和第 2 行:

print(df.drop([0, 2]))

输出为:

   A    B   C
1  2  6.0  11
3  4  8.0  13
4  4  8.0  13

条件删除

其实就是选择那些不满足条件的数据,比如继续使用上面示例中的数据,运行下面代码将会删除列 'A' 中值等于 4 的行:

print(df[df['A'] != 4])

输出为:

   A    B   C
0  1  5.0  10
1  2  6.0  11
2  3  NaN  12

Pandas 还有一个专门的函数 df.dropna() 用于删除所有带 NaN 的行:

print(df.dropna())

输出为:

   A    B   C
0  1  5.0  10
1  2  6.0  11
3  4  8.0  13
4  4  8.0  13

df.drop_duplicates() 函数用于删除所有重复的行

print(df.drop_duplicates())

输出为:

   A    B   C
   A    B   C
0  1  5.0  10
1  2  6.0  11
2  3  NaN  12
3  4  8.0  13

数据合并

Pandas 有多种方法用于合并两个或多个 DataFrame,上文提到的 concat 是最基础的方法,它简单的把两个 DataFrame 拼接到一起。此外,还可以使用 merge() 和 join() 方法。这两种方法都类似于 SQL 中的 JOIN 操作,根据一个或多个键将行合并两个 DataFrame。区别在于,merge() 是按照指定的列的数据进行合并,join() 是按照索引进行合并。

pd.merge(left, right, on=None, left_on=None, right_on=None, how='inner') 函数的参数如下:

  • left 和 right:要合并的两个DataFrame。

  • on:用于连接的列名。如果未指定,则 Pandas 会使用两个 DataFrame 中的共同列名。

  • left_on 和 right_on:分别用于左右 DataFrame 的连接键,在列名不同时使用。

  • how:指定如何合并。取值可以是 'inner'(内连接,这是默认值,表示只保留左右表格都存在的行)、'outer'(外连接,表示保留所有的行)、'left'(左连接,表示保留左表的行)或 'right'(右连接,保留右表的行)。

import pandas as pd

df1 = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                    'A': ['A0', 'A1', 'A2', 'A3']})

df2 = pd.DataFrame({'key': ['K0', 'K1', 'K4', 'K5'],
                    'B': ['B0', 'B1', 'B4', 'B5']})

result = pd.merge(df1, df2, on='key')

print(result)

上面的程序,按照 'key' 列的数据进行合并。如果某一行的 'key' 列的值在两个表中都存在,则这一行会出现在合并结果中。合并结果包含了两个表所有的列。合并结果如下:

  key   A   B
0  K0  A0  B0
1  K1  A1  B1

DataFrame.join(other, on=None, how='left') 方法的参数如下:

  • other:一个或多个要加入的DataFrame。

  • on:用于连接的列名或索引级别名称。如果要根据列进行连接,则该列必须是索引列。

  • how:指定如何合并。取值可以是'left'(默认)、'right'、'outer'或'inner',用法与 merge() 函数中的 how 参数相同。

import pandas as pd

df3 = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
                    'B': ['B0', 'B1', 'B2']},
                   index=['K0', 'K1', 'K2'])

df4 = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
                    'D': ['D0', 'D2', 'D3']},
                   index=['K0', 'K2', 'K3'])

result = df3.join(df4, how='outer')

print(result)

上面的程序,按照索引进行数据合并。因为合并方式选择了 'outer',左右两个表的所有行都会出现在结果表中。缺失的数据用 NaN 表示,比如 K1 这一行数据,只在左表中有,原本右表的列中就不会有它的数据,所有 C,D 列中,它的数据是 NaN。合并结果如下:

      A    B    C    D
K0   A0   B0   C0   D0
K1   A1   B1  NaN  NaN
K2   A2   B2   C2   D2
K3  NaN  NaN   C3   D3

数据分组

数据的分组通常是通过 groupby 方法来实现的。它可以按照某个或某些列的值对数据进行分组,然后对每个分组应用聚合函数、转换函数或过滤操作。groupby 方法 DataFrame.groupby(by=None, axis=0, ...) 有两个主要参数: by,用作分组依据的列名或列名列表,也可以是映射或函数; axis 表示分组的轴,默认为 0,按行分组,1 表示按列分组。

假设我们有以下 DataFrame:

import pandas as pd

data = {'Name': ['典韦', '许褚', '典韦', '许褚', '甘宁'],
        'Kingdom': ['魏', '魏', '魏', '魏', '吴'],
        'Score': [9, 8, 5.5, 8.5, 7]}
df = pd.DataFrame(data)

接下来,我们可以按照 Name 列对数据进行分组:

grouped = df.groupby('Name')

grouped 是分组结果,接下来我们可以对这个分组结果进行各种统计,比如求和(sum() 函数)、平均值(mean())、最大值(max())、方差(std())等:

# 计算每个名字的平均分数
mean_scores = grouped.mean()
print(mean_scores)

# 输出:
#       Score
# Name       
# 典韦     7.25
# 甘宁     7.00
# 许褚     8.25

我们可以按多个列的组合进行分组,比如可以按照 Name 和 Kingdom 分组:


grouped = df.groupby(['Name', 'Kingdom'])

# 计算每个名字的平均分数
mean_scores = grouped.mean()
print(mean_scores)

# 输出:
#               Score
# Name Kingdom       
# 典韦   魏         7.25
# 甘宁   吴         7.00
# 许褚   魏         8.25

使用 agg 方法,我们可以同时对分组后的数据应用多种聚合函数:

grouped = df.groupby('Name')
grouped_agg = grouped.agg({'Score': ['mean', 'min', 'max']})
print(grouped_agg)

# 输出:
#      Score          
#       mean  min  max
# Name                
# 典韦    7.25  5.5  9.0
# 甘宁    7.00  7.0  7.0
# 许褚    8.25  8.0  8.5

除了聚合,还可以对分组数据进行转换(transform)和过滤(filter):

# 标准化每个分组的分数
score_standardized = grouped['Score'].transform(lambda x: (x - x.mean()) / x.std())

# 仅保留平均分超过某个阈值的分组
grouped_filter = grouped.filter(lambda x: x['Score'].mean() > 6)

数据的重塑

数据重塑(reshaping)指的是重新排列现有数据的结构,以便获得更适合特定分析或操作的数据格式。常用的数据重塑方法包括透视、堆叠和融合。

透视

pivot 方法可以重排 DataFrame,生成一个“透视表”,类似于 Excel 中的透视表。DataFrame.pivot(index=None, columns=None, values=None) 方法优三个主要参数: index 表示新 DataFrame 的索引名; columns 表示新 DataFrame 的列名; values 表示填充到新 DataFrame 的值。

假设有以下DataFrame:

import pandas as pd

df = pd.DataFrame({
    'date': ['2020-01-01', '2020-01-01', '2020-01-02', '2020-01-02'],
    'variable': ['A', 'B', 'A', 'B'],
    'value': [1, 2, 3, 4]
})
print(df)

# 使用pivot生成透视表:
df_pivot = df.pivot(index='date', columns='variable', values='value')
print(df_pivot)

运行上面示例,df 中的数据是:

         date variable  value
0  2020-01-01        A      1
1  2020-01-01        B      2
2  2020-01-02        A      3
3  2020-01-02        B      4

df_pivot 中的数据是:

variable    A  B
date            
2020-01-01  1  2
2020-01-02  3  4

堆叠和取消堆叠

stack 和 unstack 方法用于将 DataFrame 的列转换为行(堆叠),或将行转换为列(取消堆叠)。将列转换为行,会产生一个 MultiIndex 的 Series。

# 将列转换为行
stacked = df.stack()
print(stacked)

# 将行转换为列
unstacked = stacked.unstack()

运行上面程序,stacked 中的数据是:

0  date        2020-01-01
   variable             A
   value                1
1  date        2020-01-01
   variable             B
   value                2
2  date        2020-01-02
   variable             A
   value                3
3  date        2020-01-02
   variable             B
   value                4
dtype: object

融合

融合(Melt)是一种数据重塑技术,用于将数据从宽格式(wide format)转换为长格式(long format)。这个操作通常用于准备数据,以便于分析、绘图或其他特定类型的处理。

  • 宽格式(Wide Format):在这种格式中,每个主题(例如时间点、实体等)的观测值分布在多个列中。

  • 长格式(Long Format):在这种格式中,每行是一个观测值,包含一个或多个标识符(ID)列和一个变量值列。每个主题的多个观测值会分布在多行中。

pd.melt(frame, id_vars=None, value_vars=None, var_name=None, value_name='value') 函数的参数包括:

  • frame:要融合的DataFrame。

  • id_vars:在融合过程中保持不变的列名(标识符列)。

  • value_vars:要被融化成行的列名。

  • var_name:融化后,包含原列名的新列的名称。

  • value_name:融化后,包含值的新列的名称。


import pandas as pd

df = pd.DataFrame({
    'Date': ['2021-01-01', '2021-01-02'],
    'Temperature': [32, 35],
    'Humidity': [80, 85]
})

# 使用melt转换为长格式
df_melted = pd.melt(df, id_vars=['Date'], var_name='Variable', value_name='Value')
print(df_melted)

运行上面程序中,df_melted 中的数据为:

         Date      Variable  Value
0  2021-01-01  Temperature     32
1  2021-01-02  Temperature     35
2  2021-01-01     Humidity     80
3  2021-01-02     Humidity     85

数据整理

数据整理是将原始数据转换为易于分析的格式的过程。它包括处理缺失值、重复数据、数据类型转换、数据规范化等多个方面。

处理缺失值

处理缺失值是数据清洁的一个重要方面。缺失值可能以 NaN(Not a Number)、None 或其他形式存在于数据中。正确处理缺失值对于进行有效的数据分析至关重要。

在处理缺失值之前,首先要识别数据中的缺失值。可以使用 isna() 或 isnull() 方法检查是否有缺失值。

import pandas as pd

data = {
    'A': [1, 2, 3, 4, 4],
    'B': [5, 6, None, 8, 8],
    'C': [10, 11, 12, 13, 13]
}
df = pd.DataFrame(data)
print(df.isna().sum())

运行上面的程序,输出:

A    0
B    1
C    0
dtype: int64

对于缺失值最简单的方法是删除,比如下面的程序:

# 删除含有缺失值的行
df.dropna(axis=0, inplace=True)

# 删除含有缺失值的列
df.dropna(axis=1, inplace=True)

数据量少的时候,我们可能不希望删除任何数据。这时候可以考虑填充缺失值。最简单的是用常数进行填充: df.fillna(value, inplace=True)。不过实际项目中更常用的是用平均值或中位数进行填充:

df.fillna(df.mean(), inplace=True)    # 用平均值填充
df.fillna(df.median(), inplace=True)  # 用中位数填充

删除重复数据

重复数据可能影响分析结果,可以使用上文介绍过的drop_duplicates()方法删除重复的行,Series对象除了drop_duplicates() 方法还有unique() 方法

数据类型转换

astype() 是进行数据类型转换的主要方法。它可以将 DataFrame 的某一列或整个 DataFrame 的数据类型转换为指定的类型。比如:

import pandas as pd

df = pd.DataFrame({'A': ['1', '2', '3'], 'B': [4, 5, 6]})

# 将列 'A' 从字符串转换为整数
df['A'] = df['A'].astype(int)

# 将多个列转换为不同的数据类型
df = df.astype({'A': int, 'B': float})

数据规范化

数据规范化是指将数据转换为标准格式,以便于分析和处理。数据规范化通常包括以下几个方面:

字符串规范化

字符串数据可能包含多种格式,需要统一规范化。常见的字符串规范化包括大小写转换、去除空格等:

df['column'] = df['column'].str.lower()  # 转换为小写
df['column'] = df['column'].str.upper()  # 转换为大写

df['column'] = df['column'].str.strip()  # 去除两端空格

# 替换文本
df['column'] = df['column'].str.replace('old_text', 'new_text')

数值规范化

对于数值型数据,规范化通常包括缩放和转换操作,使数据符合特定的范围或格式。常见的数值规范化方法包括:


# 将数据缩放到 0 和 1 之间
df['normalized'] = (df['column'] - df['column'].min()) / (df['column'].max() - df['column'].min())

# 数据按均值为 0,标准差为 1 的分布进行缩放
df['standardized'] = (df['column'] - df['column'].mean()) / df['column'].std()

日期时间规范化

日期和时间数据可能以多种格式存在,需要转换为统一的格式。


# 转换日期格式
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

# 提取日期组件,如年、月、日、小时等
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month

类别数据规范化

对于类别数据,确保所有类别都是统一的,无误差和重复。

# 统一类别名称,使用映射或替换方法
category_map = {'cat1': 'Category 1', 'cat2': 'Category 2'}
df['category'] = df['category'].map(category_map)

# 转换为类别类型
df['category'] = df['category'].astype('category')

创建派生变量

创建派生变量(也称为特征工程)指的是从现有数据中生成新列,这些新列通常是现有列的某种数学运算、逻辑运算的结果,或者是更复杂的函数变换。派生变量对于数据分析、可视化甚至机器学习模型的构建都非常重要。

基于数学运算

可以对 DataFrame 中的列执行各种数学运算,以创建新的派生列。

import pandas as pd

df = pd.DataFrame({
    'A': [1, 2, 3, 4],
    'B': [5, 6, 7, 8]
})

# 创建一个新列,为A和B的和
df['C'] = df['A'] + df['B']

# 创建一个新列,为A的平方
df['A_squared'] = df['A'] ** 2

基于条件的派生变量

# 创建一个新列,基于A列的值
df['A_greater_than_2'] = df['A'] > 2

重命名列

为了提高可读性,有时需要重命名 DataFrame 的列。rename 方法是重命名列的主要方式。你可以通过传递一个映射字典来指定旧列名到新列名的映射。

import pandas as pd

df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6]
})

# 重命名单个列
df.rename(columns={'A': 'a'}, inplace=True)

# 重命名多个列
df.rename(columns={'B': 'b', 'a': 'alpha'}, inplace=True)

另一种重命名列的方法是直接修改 DataFrame 的 columns 属性。

df.columns = ['new_name1', 'new_name2']

这种方法在你知道所有列名并且想全部替换时很方便。

set_axis方法也可以用来重命名列,它允许你同时设置行索引和列名。

df = df.set_axis(['new_name1', 'new_name2'], axis=1, inplace=False)

数据排序

数据排序是一种基本的数据操作,用于根据一个或多个列的值对数据进行排列。排序可以是升序或降序。以下是DataFrame数据排序的主要方法和应用:

sort_values 是 Pandas 中最常用的排序方法。它可以根据一个或多个列的值对 DataFrame 进行排序。

import pandas as pd

df = pd.DataFrame({
    'A': [3, 1, 2],
    'B': [6, 5, 4]
})

# 单列排序,根据列'A'升序排序
df_sorted = df.sort_values(by='A')


# 多列排序,根据列'A'升序和列'B'降序排序
df_sorted = df.sort_values(by=['A', 'B'], ascending=[True, False])