【数据分析】03 - pandas

【数据分析】03 - pandas

文章目录

一:Pandas入门

1:Pandas概述

Pandas 一个强大的分析结构化数据的工具集,基础是 numpy

Pandas 官网 https://pandas.pydata.org/

Pandas 源代码:https://github.com/pandas-dev/pandas

  • Pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。
  • Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。
  • Pandas 广泛应用在学术、金融、统计学等各个数据分析领域。

Pandas 是数据分析的利器,它不仅提供了高效、灵活的数据结构,还能帮助你以极低的成本完成复杂的数据操作和分析任务。

Pandas 提供了丰富的功能,包括:

  • 数据清洗:处理缺失数据、重复数据等。
  • 数据转换:改变数据的形状、结构或格式。
  • 数据分析:进行统计分析、聚合、分组等。
  • 数据可视化:通过整合 Matplotlib 和 Seaborn 等库,可以进行数据可视化。
shell 复制代码
# 安装pandas
pip install pandas
python 复制代码
# 测试pandas的安装
import pandas as pd

def pandas_test01():
    print(pd.__version__)


if __name__ == '__main__':
    pandas_test01()

pandas的特点如下

高效的数据结构:

  • Series:一维数据结构,类似于列表(List),但拥有更强的功能,支持索引。
  • DataFrame:二维数据结构,类似于表格或数据库中的数据表,行和列都具有标签(索引)。

数据清洗与预处理:

  • Pandas 提供了丰富的函数来处理缺失值、重复数据、数据类型转换、字符串操作等,帮助用户轻松清理和转换数据。

数据操作与分析:

  • 支持高效的数据选择、筛选、切片,按条件提取数据、合并、连接多个数据集、数据分组、汇总统计等操作。
  • 可以进行复杂的数据变换,如数据透视表、交叉表、时间序列分析等。

数据读取与导出:

  • 支持从各种格式的数据源读取数据,如 CSV、Excel、JSON、SQL 数据库等。
  • 也可以将处理后的数据导出为不同格式,如 CSV、Excel 等。

数据可视化:

  • 通过与 Matplotlib 和其他可视化工具的集成,Pandas 可以快速生成折线图、柱状图、散点图等常见图表。

时间序列分析:

  • 支持强大的时间序列处理功能,包括日期的解析、重采样、时区转换等。

性能与优化:

  • Pandas 优化了大规模数据处理,提供高效的向量化操作,避免了使用 Python 循环处理数据的低效。
  • 还支持一些内存优化技术,比如使用 category 类型处理重复的数据。

2:Pands数据结构

Series + DataFrame


DataFrame 可视为由多个 Series 组成的数据结构:

  • index是自动生成的,表示记录1 ~ 记录n
  • 每一列表示一个Series, 列的名称就是key

DataFrame = key + value + index

python 复制代码
import pandas as pd

def pandas_test01():
    print(pd.__version__)


def pandas_test02():
    # 创建两个Series对象
    series_apples = pd.Series([1, 3, 7, 4])
    series_bananas = pd.Series([2, 6, 3, 5])

    # 将两个Series对象相加,得到DataFrame,并指定列名
    df = pd.DataFrame({'Apples': series_apples, 'Bananas': series_bananas})

    # 显示DataFrame
    print(df)


if __name__ == '__main__':
    pandas_test02()

二:输入格式详解

1:CSV

CSV 是一种通用的、相对简单的文件格式,被用户、商业和科学广泛应用。

1.1:基本使用

pandas和CSV之间就是两个方法:

  • 从CSV中读取数据,并加载成为DataFrame -> pd.read_csv()
  • 将DataFrame中的内容写入到CSV -> DataFrame.to_csv()

read_csv 常用参数:

参数 说明 默认值
filepath_or_buffer CSV 文件的路径或文件对象(支持 URL、文件路径、文件对象等) 必需参数
sep 定义字段分隔符,默认是逗号(,),可以改为其他字符,如制表符(\t ','
header 指定行号作为列标题,默认为 0(表示第一行),或者设置为 None 没有标题 0
names 自定义列名,传入列名列表 None
index_col 用作行索引的列的列号或列名 None
usecols 读取指定的列,可以是列的名称或列的索引 None
dtype 强制将列转换为指定的数据类型 None
skiprows 跳过文件开头的指定行数,或者传入一个行号的列表 None
nrows 读取前 N 行数据 None
na_values 指定哪些值应视为缺失值(NaN) None
skipfooter 跳过文件结尾的指定行数 0
encoding 文件的编码格式(如 utf-8latin1 等) None

to_csv 常用参数:

参数 说明 默认值
path_or_buffer CSV 文件的路径或文件对象(支持文件路径、文件对象) 必需参数
sep 定义字段分隔符,默认是逗号(,),可以改为其他字符,如制表符(\t ','
index 是否写入行索引,默认 True 表示写入索引 True
columns 指定写入的列,可以是列的名称列表 None
header 是否写入列名,默认 True 表示写入列名,设置为 False 表示不写列名 True
mode 写入文件的模式,默认是 w(写模式),可以设置为 a(追加模式) 'w'
encoding 文件的编码格式,如 utf-8latin1 None
line_terminator 定义行结束符,默认为 \n None
quoting 设置如何对文件中的数据进行引号处理(0-3,具体引用方式可查文档) None
quotechar 设置用于引用的字符,默认为双引号 " '"'
date_format 自定义日期格式,如果列包含日期数据,则可以使用此参数指定日期格式 None
doublequote 如果为 True,则在写入时会将包含引号的文本使用双引号括起来 True
python 复制代码
import pandas as pd

# 读取 CSV 文件,并自定义列名和分隔符
df = pd.read_csv('data.csv', sep=';', header=0, names=['A', 'B', 'C'], dtype={'A': int, 'B': float})
print(df)

# 假设 df 是一个已有的 DataFrame
df.to_csv('output.csv', index=False, header=True, columns=['A', 'B'])
1.2:数据处理

2:Excel

操作 方法 说明
读取 Excel 文件 pd.read_excel() 读取 Excel 文件,返回 DataFrame
将 DataFrame 写入 Excel DataFrame.to_excel() 将 DataFrame 写入 Excel 文件
加载 Excel 文件 pd.ExcelFile() 加载 Excel 文件并访问多个表单
使用 ExcelWriter 写多个表单 pd.ExcelWriter() 写入多个 DataFrame 到同一 Excel 文件的不同表单
2.1:读取文件

读取excel方法的参数说明

参数 描述
io 这是必需的参数,指定了要读取的 Excel 文件的路径或文件对象。
sheet_name=0 指定要读取的工作表名称或索引。默认为0,即第一个工作表。
header=0 指定用作列名的行。默认为0,即第一行。
names=None 用于指定列名的列表。如果提供,将覆盖文件中的列名。
index_col=None 指定用作行索引的列。可以是列的名称或数字。
usecols=None 指定要读取的列。可以是列名的列表或列索引的列表。
dtype=None 指定列的数据类型。可以是字典格式,键为列名,值为数据类型。
engine=None 指定解析引擎。默认为None,pandas 会自动选择。
converters=None 用于转换数据的函数字典。
true_values=None 指定应该被视为布尔值True的值。
false_values=None 指定应该被视为布尔值False的值。
skiprows=None 指定要跳过的行数或要跳过的行的列表。
nrows=None 指定要读取的行数。
na_values=None 指定应该被视为缺失值的值。
keep_default_na=True 指定是否要将默认的缺失值(例如NaN)解析为NA。
na_filter=True 指定是否要将数据转换为NA。
verbose=False 指定是否要输出详细的进度信息。
parse_dates=False 指定是否要解析日期。
date_parser=<no_default> 用于解析日期的函数。
date_format=None 指定日期的格式。
thousands=None 指定千位分隔符。
decimal='.' 指定小数点字符。
comment=None 指定注释字符。
skipfooter=0 指定要跳过的文件末尾的行数。
storage_options=None 用于云存储的参数字典。
dtype_backend=<no_default> 指定数据类型后端。
engine_kwargs=None 传递给引擎的额外参数字典。
python 复制代码
import pandas as pd

# 读取默认的第一个表单
df = pd.read_excel('data.xlsx')
print(df)

# 读取指定表单的内容(表单名称)
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
print(df)

# 读取多个表单,返回一个字典
dfs = pd.read_excel('data.xlsx', sheet_name=['Sheet1', 'Sheet2'])
print(dfs)

# 自定义列名并跳过前两行
df = pd.read_excel('data.xlsx', header=None, names=['A', 'B', 'C'], skiprows=2)
print(df)
2.2:写入文件

to_excel()方法的参数说明

参数 描述
excel_writer 这是必需的参数,指定了要写入的 Excel 文件路径或文件对象。
sheet_name='Sheet1' 指定写入的工作表名称,默认为 'Sheet1'。
na_rep='' 指定在 Excel 文件中表示缺失值(NaN)的字符串,默认为空字符串。
float_format=None 指定浮点数的格式。如果为 None,则使用 Excel 的默认格式。
columns=None 指定要写入的列。如果为 None,则写入所有列。
header=True 指定是否写入列名作为第一行。如果为 False,则不写入列名。
index=True 指定是否写入索引作为第一列。如果为 False,则不写入索引。
index_label=None 指定索引列的标签。如果为 None,则不写入索引标签。
startrow=0 指定开始写入的行号,默认从第0行开始。
startcol=0 指定开始写入的列号,默认从第0列开始。
engine=None 指定写入 Excel 文件时使用的引擎,默认为 None,pandas 会自动选择。
merge_cells=True 指定是否合并单元格。如果为 True,则合并具有相同值的单元格。
inf_rep='inf' 指定在 Excel 文件中表示无穷大值的字符串,默认为 'inf'。
freeze_panes=None 指定冻结窗格的位置。如果为 None,则不冻结窗格。
storage_options=None 用于云存储的参数字典。
engine_kwargs=None 传递给引擎的额外参数字典。
python 复制代码
import pandas as pd

# 创建一个简单的 DataFrame
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],  # 第一列
'Age': [25, 30, 35], # 第二列
'City': ['New York', 'Los Angeles', 'Chicago'] # 第三列
})

# 将 DataFrame 写入 Excel 文件,写入 'Sheet1' 表单
df.to_excel('output.xlsx', sheet_name='Sheet1', index=False)

# 写入多个表单,使用 ExcelWriter
with pd.ExcelWriter('output2.xlsx') as writer:
    df.to_excel(writer, sheet_name='Sheet1', index=False)
    df.to_excel(writer, sheet_name='Sheet2', index=False)
2.3:加载文件

ExcelFile - 加载 Excel 文件

python 复制代码
import pandas as pd

# 使用 ExcelFile 加载 Excel 文件
excel_file = pd.ExcelFile('data.xlsx')

# 查看所有表单的名称
print(excel_file.sheet_names)

# 读取指定的表单
df = excel_file.parse('Sheet1')
print(df)

# 关闭文件
excel_file.close()
2.4:多个表单的写入

可以使用ExcelWriter写入df到指定excel的指定sheet中

python 复制代码
df1 = pd.DataFrame([["AAA", "BBB"]], columns=["Spam", "Egg"])  
df2 = pd.DataFrame([["ABC", "XYZ"]], columns=["Foo", "Bar"])  
with pd.ExcelWriter("path_to_file.xlsx") as writer:
    df1.to_excel(writer, sheet_name="Sheet1")  
    df2.to_excel(writer, sheet_name="Sheet2")

3:JSON

操作 方法 说明
从 JSON 文件/字符串读取数据 pd.read_json() 从 JSON 数据中读取并加载为 DataFrame
将 DataFrame 转换为 JSON DataFrame.to_json() 将 DataFrame 转换为 JSON 格式的数据,可以指定结构化方式
支持 JSON 结构化方式 orient 参数 支持多种结构化方式,如 splitrecordscolumns
3.1:读取Json文件
python 复制代码
import pandas as pd

df = pd.read_json(
    path_or_buffer,      # JSON 文件路径、JSON 字符串或 URL
    orient=None,         # JSON 数据的结构方式,默认是 'columns'
    dtype=None,          # 强制指定列的数据类型
    convert_axes=True,   # 是否转换行列索引
    convert_dates=True,  # 是否将日期解析为日期类型
    keep_default_na=True # 是否保留默认的缺失值标记
)
rient 值 JSON 格式示例 描述
split {"index":["a","b"],"columns":["A","B"],"data":[[1,2],[3,4]]} 使用键 indexcolumnsdata 结构
records [{"A":1,"B":2},{"A":3,"B":4}] 每个记录是一个字典,表示一行数据
index {"a":{"A":1,"B":2},"b":{"A":3,"B":4}} 使用索引为键,值为字典的方式
columns {"A":{"a":1,"b":3},"B":{"a":2,"b":4}} 使用列名为键,值为字典的方式
values [[1,2],[3,4]] 只返回数据,不包括索引和列名
3.2:DF读取为JSON
python 复制代码
df.to_json(
    path_or_buffer=None,    # 输出的文件路径或文件对象,如果是 None 则返回 JSON 字符串
    orient=None,            # JSON 格式方式,支持 'split', 'records', 'index', 'columns', 'values'
    date_format=None,       # 日期格式,支持 'epoch', 'iso'
    default_handler=None,   # 自定义非标准类型的处理函数
    lines=False,            # 是否将每行数据作为一行(适用于 'records' 或 'split')
    encoding='utf-8'        # 编码格式
)

三:Pandas操作

1:基本方法

1.1:Series的基本方法
方法 描述
axes 返回行轴标签的列表
dtype 返回对象的dtype。
empty 如果Series为空,则返回True。
ndim 根据定义返回基础数据的维数。
size 返回基础数据中的元素数。
values 将Series返回为ndarray。
head() 返回前n行。
tail() 返回最后n行。
python 复制代码
import pandas as pd
import numpy as np

if __name__ == '__main__':
    ser = pd.Series(np.random.randn(4))
    print(ser)
    print(ser.axes)  # 返回轴标签的列表 [RangeIndex(start=0, stop=4, step=1)]
    print(ser.empty)  # 返回一个布尔值,判断该对象是否为空, 值为false
    print(ser.ndim)  # 返回维度,值为1
    print(ser.size)  # 返回元素个数,值为4
    print(ser.values) # 返回数组,值为[-0.37214805  0.35749305 -0.11696405  0.11696405]
    print(ser.head()) # 默认返回前5行数据,可以自定义数据
    print(ser.tail()) # 默认返回最后5行数据,可以自定义数据
1.2:DataFrame的基本方法
属性/方法 描述
T 行和列互相转换
axes 返回以行轴标签和列轴标签为唯一成员的列表。
dtypes 返回此对象中的dtypes。
empty 如果NDFrame完全为空[没有项目],则为true;否则为false。如果任何轴的长度为0。
ndim 轴数/数组尺寸。
shape 返回表示DataFrame维度的元组。
size NDFrame中的元素数。
values NDFrame的数字表示。
head() 返回前n行。
tail() 返回最后n行。
python 复制代码
import pandas as pd
import numpy as np

if __name__ == '__main__':
    d = {'Name': pd.Series(['A', 'B', 'C', 'D', 'E', 'F', 'G']),
         'Age': pd.Series([25, 26, 25, 23, 30, 29, 23]),
         'Rating': pd.Series([4.23, 3.24, 3.98, 2.56, 3.20, 4.6, 3.8])}
    # 创建一个 DataFrame
    df = pd.DataFrame(d)
    print(df)
    print("数据序列的转置是:")
    print(df.T)
    print("行轴标签和列轴标签是:")
    print(df.axes)
    print("每列的数据类型如下:")
    print(df.dtypes)
    print("Is the object empty?")
    print(df.empty)
    print("The dimension of the object is:")
    print(df.ndim)
    print("The shape of the object is:")
    print(df.shape)
    print("The total number of elements in our object is:")
    print(df.size)
    print("The actual data in our data frame is:")
    print(df.values)
    print("The first two rows of the data frame is:")
    print(df.head(2))
    print("The last two rows of the data frame is:")
    print(df.tail(2))
复制代码
  Name  Age  Rating
0    A   25    4.23
1    B   26    3.24
2    C   25    3.98
3    D   23    2.56
4    E   30    3.20
5    F   29    4.60
6    G   23    3.80
数据序列的转置是:
           0     1     2     3    4    5    6
Name       A     B     C     D    E    F    G
Age       25    26    25    23   30   29   23
Rating  4.23  3.24  3.98  2.56  3.2  4.6  3.8
行轴标签和列轴标签是:
[RangeIndex(start=0, stop=7, step=1), Index(['Name', 'Age', 'Rating'], dtype='object')]
每列的数据类型如下:
Name       object
Age         int64
Rating    float64
dtype: object
Is the object empty?
False
The dimension of the object is:
2
The shape of the object is:
(7, 3)
The total number of elements in our object is:
21
The actual data in our data frame is:
[['A' 25 4.23]
 ['B' 26 3.24]
 ['C' 25 3.98]
 ['D' 23 2.56]
 ['E' 30 3.2]
 ['F' 29 4.6]
 ['G' 23 3.8]]
The first two rows of the data frame is:
  Name  Age  Rating
0    A   25    4.23
1    B   26    3.24
The last two rows of the data frame is:
  Name  Age  Rating
5    F   29     4.6
6    G   23     3.8

2:描述性统计

DataFrame用在大量的计算描述性信息统计和其他相关操作。其中大多数是聚合,例如sum(),mean()

一般而言,这些方法采用轴参数,就像ndarray。{sum,std,...}一样,但是可以通过名称或整数指定轴

1.1:sum()

返回所请求轴的值之和。默认情况下,轴为索引(轴=0)

python 复制代码
import pandas as pd
import numpy as np

d = {'Name':pd.Series(['A','B','C','D','E','F','G', 'H','I','J','K','L']),
    'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),
    'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])
 }

# 字典series -> dataframe
df = pd.DataFrame(d)

# 根据索引求和
print(df.sum())

print(df)
复制代码
/Users/cuihaida/project/python/first/venv/bin/python /Users/cuihaida/project/python/first/my-data-anaylze/my_pandas03.py 
Name      ABCDEFGHIJKL
Age                382
Rating           44.92
dtype: object
   Name  Age  Rating
0     A   25    4.23
1     B   26    3.24
2     C   25    3.98
3     D   23    2.56
4     E   30    3.20
5     F   29    4.60
6     G   23    3.80
7     H   34    3.78
8     I   40    2.98
9     J   30    4.80
10    K   51    4.10
11    L   46    3.65

Process finished with exit code 0
1.2:mean()

返回平均值

python 复制代码
import pandas as pd
import numpy as np

d = {'Name':pd.Series(['A','B','C','D','E','F','G', 'H','I','J','K','L']),
    'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),
    'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])
 }

# 字典series -> dataframe
df = pd.DataFrame(d)

# 根据索引求和
print(df.sum())
print("=" * 20)
print(df.sum(axis=1, numeric_only=True))  # age + rating
print("=" * 20)

# 返回平均值
print(df.mean(axis=0, skipna=True, numeric_only=True))
print("=" * 20)
print(df)
复制代码
/Users/cuihaida/project/python/first/venv/bin/python /Users/cuihaida/project/python/first/my-data-anaylze/my_pandas03.py 
Name      ABCDEFGHIJKL
Age                382
Rating           44.92
dtype: object
====================
0     29.23
1     29.24
2     28.98
3     25.56
4     33.20
5     33.60
6     26.80
7     37.78
8     42.98
9     34.80
10    55.10
11    49.65
dtype: float64
====================
Age       31.833333
Rating     3.743333
dtype: float64
====================
   Name  Age  Rating
0     A   25    4.23
1     B   26    3.24
2     C   25    3.98
3     D   23    2.56
4     E   30    3.20
5     F   29    4.60
6     G   23    3.80
7     H   34    3.78
8     I   40    2.98
9     J   30    4.80
10    K   51    4.10
11    L   46    3.65

Process finished with exit code 0
1.3:其他统计性函数
编号 方法 描述
1 count() 非空数
2 sum() 总数
3 mean() 平均数
4 median() 中位数
5 mode() 模式
6 std() 标准差
7 min() 最低值
8 max() 最大值
9 abs() 绝对值
10 prod() 乘积
11 cumsum() 累加
12 cumprod() 累乘

由于DataFrame是异构数据结构。泛型运算并不适用于所有功能,注意下面两点:

  • 诸如sum(),cumsum()之类的函数可用于数字和字符(或)字符串数据元素,而不会出现任何错误。虽然字符集合从不普遍使用,但不会抛出任何异常。
  • 当DataFrame包含字符或字符串数据时,诸如abs(),cumprod()之类的函数将引发异常,因为此类操作无法执行。
1.4:汇总函数describe

此函数提供平均值,std和IQR值。并且,函数不包括字符列和有关数字列的给定摘要

同时这个函数还支持一个include参数,用于传递有关汇总时需要考虑哪些列的必要信息的参数。取值列表;默认情况下为"数字"。

  • object − 汇总字符串列
  • number − 汇总数字列
  • all − 总结所有列在一起(不应该把它作为一个列表值)
python 复制代码
import pandas as pd
import numpy as np

if __name__ == '__main__':
    d = {'Name': pd.Series(['A', 'B', 'C', 'D', 'E', 'F', 'G']),
         'Age': pd.Series([25, 26, 25, 23, 30, 29, 23]),
         'Rating': pd.Series([4.23, 3.24, 3.98, 2.56, 3.20, 4.6, 3.8])}
    df = pd.DataFrame(d)
    # 查看数据集的描述信息, include -> object,number,all
    # object -> object类型的数据, 就是字符串列
    # number -> 数值列, 默认
    # all -> 所有列
    print(df.describe(include=['number']))

3:函数的应用

要将您自己或其他库的函数应用于Pandas对象,您应该了解三个重要的方法:

  • 表函数应用程序:pipe()
  • 行或列函数应用程序:apply()
  • 元素级函数应用程序:applymap()

要使用的适当方法取决于您的函数是希望对整个数据帧进行操作,还是行操作还是按列操作,还是按元素操作

3.1:表函数应用程序

可以通过传递函数和适当数量的参数作为管道参数来执行对DataFrame自定义操作,使用的方法是pipe()

python 复制代码
import pandas as pd
import numpy as np

# 定义一个操作函数,到时候会让dataFrame应用这个函数
def adder(ele1, ele2):
    return ele1 + ele2

df = pd.DataFrame(np.random.randn(5,3),columns=['col1','col2','col3'])
print(df)
print("=" * 20)
ans = df.pipe(adder, 2) # 使用pipe()进行表函数应用, 对每一个元素 + 2
print(ans)
复制代码
       col1      col2      col3
0 -1.743730  0.426996 -3.014761
1  0.209032 -0.407826  2.828854
2 -0.019639  0.879595  1.143649
3  0.155613 -0.562366 -3.301372
4  1.005468 -0.673557 -0.449234
====================
       col1      col2      col3
0  0.256270  2.426996 -1.014761
1  2.209032  1.592174  4.828854
2  1.980361  2.879595  3.143649
3  2.155613  1.437634 -1.301372
4  3.005468  1.326443  1.550766
3.2:行或列函数应用程序

可以使用apply()方法沿DataFrame或Panel的轴应用任意函数,该方法与描述性统计方法一样,采用可选的axis参数

默认情况下,该操作按列执行,将每一列视为类似数组的形式。

python 复制代码
import pandas as pd
import numpy as np

# 定义一个操作函数,到时候会让dataFrame应用这个函数
def adder(ele1,ele2):
    return ele1+ele2

df = pd.DataFrame(np.random.randn(5,3),columns=['col1','col2','col3'])
print(df)
print(df.apply(np.mean)) # 列求平均
print(df.apply(np.mean,axis=1)) # 行求平均
复制代码
       col1      col2      col3
0 -0.123136 -0.512380 -0.258701
1  0.775471 -0.783096 -0.580526
2 -0.994060 -0.593498 -0.293823
3 -1.039347  0.523736 -0.062395
4 -0.378043 -0.651012 -0.509351
col1   -0.351823
col2   -0.403250
col3   -0.340959
dtype: float64
0   -0.298073
1   -0.196050
2   -0.627127
3   -0.192669
4   -0.512802
dtype: float64
3.3:元素级函数应用程序

并非所有函数都可以向量化(NumPy数组既不返回另一个数组,也不返回任何值)

DataFrame上的applymap() 方法和Series上的map() 类似地接受任何采用单个值并返回单个值的Python函数。

python 复制代码
import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(5, 3), columns=['col1', 'col2', 'col3'])
# 自定义函数
print(df)
print("=" * 20)
print(df['col1'].map(lambda x: x + 100)) # 对col1列的每一个数进行加1操作,不会影响原数组
复制代码
       col1      col2      col3
0  0.720981 -1.848660 -1.021995
1 -0.263746  0.328287 -1.218116
2 -0.041258  0.364516 -0.553747
3 -0.772308  0.542454 -0.385765
4 -1.347373 -0.311151  0.608969
====================
0    100.720981
1     99.736254
2     99.958742
3     99.227692
4     98.652627
Name: col1, dtype: float64

4:重建索引

4.1:reindex()

重建索引 会更改DataFrame的行标签和列标签。重新索引是指使数据与特定轴上的一组给定标签匹配。

通过索引可以完成多个操作,例如:

  • 重新排序现有数据以匹配一组新标签。

  • 在标签数据不存在的标签位置中插入缺失值(NA)标记。

python 复制代码
import pandas as pd
import numpy as np
N=20
df = pd.DataFrame({
    'A': pd.date_range(start='2016-01-01',periods=N,freq='D'),
    'x': np.linspace(0,stop=N-1,num=N),
    'y': np.random.rand(N),
    'C': np.random.choice(['Low','Medium','High'],N).tolist(),
    'D': np.random.normal(100, 10, size=(N)).tolist()
})

# DataFrame重建索引
# 找出df中的A, C, B列作为新的索引和列, 对于在df中不存在的列,默认填充NaN
df_reindexed = df.reindex(index=[0,2,5], columns=['A', 'C', 'B'])
print(df_reindexed)
复制代码
           A       C   B
0 2016-01-01  Medium NaN
2 2016-01-03     Low NaN
5 2016-01-06     Low NaN
4.2:reindex_like()

可以使用reindex_like()重新索引以与其他对象对齐

python 复制代码
import pandas as pd
import numpy as np

def test2():
    df1 = pd.DataFrame(np.random.randn(10, 3), columns=['col1', 'col2', 'col3'])
    df2 = pd.DataFrame(np.random.randn(7, 3), columns=['col1', 'col2', 'col3'])
    df1 = df1.reindex_like(df2) # df1变成df2的索引和列的结构,也是7行3列,如果多出来就切断,如果少,就填充NaN
    print(df1)

if __name__ == '__main__':
    test2()
复制代码
       col1      col2      col3
0 -0.224233  1.808778  0.105156
1 -0.729603  0.874080  0.470185
2 -1.795519  0.289359 -1.302235
3 -1.007448 -0.064831 -0.150167
4  0.694548  0.283895  0.574875
5  0.410779 -1.394223  0.376044
6 -0.447436  1.297874  0.764893

🎉 当然可以不填充NaN, 可以通过给reindex() & reindex_like()赋值method属性,决定填充信息

  • pad/ffill − 向前填充值
  • bfill/backfill − 向后填充值
  • nearest − 从最接近的索引值填充

🎉 在指定填充信息之后,还可以使用limit属性决定最多填充几行

python 复制代码
import pandas as pd
import numpy as np

def test3():
    df1 = pd.DataFrame(np.random.randn(6, 3), columns=['col1', 'col2', 'col3'])
    df2 = pd.DataFrame(np.random.randn(2, 3), columns=['col1', 'col2', 'col3'])
    # 填充 NAN
    print(df2.reindex_like(df1))
    # 现在用前面的值填充NAN
    print("带前向填充的数据帧:")
    print(df2.reindex_like(df1, method='ffill'), limit=2) # 向前填充,最多填充2行

if __name__ == '__main__':
    test3()
复制代码
       col1      col2     col3
0  0.342667  0.725107 -0.40085
1 -0.346052  0.070069 -0.03319
2       NaN       NaN      NaN
3       NaN       NaN      NaN
4       NaN       NaN      NaN
5       NaN       NaN      NaN
带前向填充的数据帧:
       col1      col2     col3
0  0.342667  0.725107 -0.40085
1 -0.346052  0.070069 -0.03319
2 -0.346052  0.070069 -0.03319
3 -0.346052  0.070069 -0.03319
4       NaN       NaN      NaN
5       NaN       NaN      NaN
4.3:重命名

通过rename()方法,您可以基于某些映射(字典或系列)或任意函数来重新标记轴

python 复制代码
import pandas as pd
import numpy as np

def test4():
    df1 = pd.DataFrame(np.random.randn(6, 3), columns=['col1', 'col2', 'col3'])
    print(df1)
    print("重命名行和列之后:")
    new = df1.rename(columns={'col1': 'c1', 'col2': 'c2'}, index={0: 'foo', 1: 'bar'})
    print(new)

if __name__ == '__main__':
    test4()
复制代码
       col1      col2      col3
0  1.985762  1.067307 -0.681527
1  0.240389  0.700743  0.051090
2 -1.677559  0.175037  0.242483
3  0.623115 -0.200200  0.216992
4  2.097055 -1.563505 -0.156450
5  0.672499  0.088381 -0.601609
重命名行和列之后:
           c1        c2      col3
foo  1.985762  1.067307 -0.681527
bar  0.240389  0.700743  0.051090
2   -1.677559  0.175037  0.242483
3    0.623115 -0.200200  0.216992
4    2.097055 -1.563505 -0.156450
5    0.672499  0.088381 -0.601609

5:迭代和排序

5.1:迭代
5.2:排序

核心就是sort_index()sort_values()

python 复制代码
import numpy as np
import pandas as pd

def test1():
    # 定义一个10行两列的dataframe, index表示每一行的索引,columns表示每一列的key
    unsorted_df = pd.DataFrame(np.random.randn(10, 2), index=[1, 4, 6, 2, 3, 5, 9, 8, 0, 7], columns = ['col2', 'col1'])
    print(unsorted_df)
    print("默认排序")
    print(unsorted_df.sort_index()) # 排序, 默认根据index的升序排序(index从0 -> 9)
    print("可以使用ascending参数控制倒排")
    print(unsorted_df.sort_index(ascending=False))  # 倒排, index从9 -> 0
    print("也可以对列进行排序, 可以通过指定axis = 1")
    print(unsorted_df.sort_index(axis=1))
    print("可以使用 sort_values() 对值进行排序, by属性是通过那个key, 可以指定多个,列表形式")
    print(unsorted_df.sort_values(by='col1'))
    print("sort_values() 提供了从mergesort归并排序,heapsort堆排序和quicksort快排中选择算法的指定")
    print("mergesort是唯一稳定的排序")
    print(unsorted_df.sort_values(by='col1', kind='mergesort'))


if __name__ == '__main__':
    test1()
复制代码
/Users/cuihaida/project/python/first/venv/bin/python /Users/cuihaida/project/python/first/my-data-anaylze/my_pandas04.py 
       col2      col1
1  0.071396 -1.011856
4 -0.218280  0.530760
6 -0.206268  0.562001
2 -0.385724  2.792641
3 -0.294598 -0.322529
5 -0.212050  0.278547
9  0.375532 -0.281603
8  0.690356  1.799434
0 -0.974060 -1.411210
7  0.502817  0.433678
默认排序
       col2      col1
0 -0.974060 -1.411210
1  0.071396 -1.011856
2 -0.385724  2.792641
3 -0.294598 -0.322529
4 -0.218280  0.530760
5 -0.212050  0.278547
6 -0.206268  0.562001
7  0.502817  0.433678
8  0.690356  1.799434
9  0.375532 -0.281603
可以使用ascending参数控制倒排
       col2      col1
9  0.375532 -0.281603
8  0.690356  1.799434
7  0.502817  0.433678
6 -0.206268  0.562001
5 -0.212050  0.278547
4 -0.218280  0.530760
3 -0.294598 -0.322529
2 -0.385724  2.792641
1  0.071396 -1.011856
0 -0.974060 -1.411210
也可以对列进行排序, 可以通过指定axis = 1
       col1      col2
1 -1.011856  0.071396
4  0.530760 -0.218280
6  0.562001 -0.206268
2  2.792641 -0.385724
3 -0.322529 -0.294598
5  0.278547 -0.212050
9 -0.281603  0.375532
8  1.799434  0.690356
0 -1.411210 -0.974060
7  0.433678  0.502817
可以使用 sort_values() 对值进行排序, by属性是通过那个key, 可以指定多个,列表形式
       col2      col1
0 -0.974060 -1.411210
1  0.071396 -1.011856
3 -0.294598 -0.322529
9  0.375532 -0.281603
5 -0.212050  0.278547
7  0.502817  0.433678
4 -0.218280  0.530760
6 -0.206268  0.562001
8  0.690356  1.799434
2 -0.385724  2.792641
sort_values() 提供了从mergesort归并排序,heapsort堆排序和quicksort快排中选择算法的指定
mergesort是唯一稳定的排序
       col2      col1
0 -0.974060 -1.411210
1  0.071396 -1.011856
3 -0.294598 -0.322529
9  0.375532 -0.281603
5 -0.212050  0.278547
7  0.502817  0.433678
4 -0.218280  0.530760
6 -0.206268  0.562001
8  0.690356  1.799434
2 -0.385724  2.792641

Process finished with exit code 0

6:series的文本处理

几乎所有这些方法都可用于Python字符串函数

因此,将Series对象转换为String对象,然后执行该操作。

方法 说明
lower() 将系列/索引中的字符串转换为小写。
upper() 将系列/索引中的字符串转换为大写。
len() 计算字符串length()。
strip() 帮助从两侧从系列/索引中的每个字符串中去除空格(包括换行符)。
split(' ') 用给定的模式分割每个字符串。
cat(sep=' ') 用给定的分隔符连接系列/索引元素。
get_dummies() 返回具有一键编码值的DataFrame。
contains(pattern) 如果子字符串包含在元素中,则为每个元素返回一个布尔值True,否则返回False。
replace(a,b) a值替换成b。
repeat(value) 以指定的次数重复每个元素。
count(pattern) 返回每个元素中模式出现的次数。
startswith(pattern) 如果系列/索引中的元素以模式开头,则返回true。
endswith(pattern) 如果系列/索引中的元素以模式结尾,则返回true。
find(pattern) 返回模式首次出现的第一个位置。
findall(pattern) 返回所有出现的模式的列表。
swapcase 大小写互换
islower() 检查"系列/索引"中每个字符串中的所有字符是否都小写。返回布尔值
isupper() 检查"系列/索引"中每个字符串中的所有字符是否都大写。返回布尔值。
isnumeric() 检查"系列/索引"中每个字符串中的所有字符是否都是数字。返回布尔值。
python 复制代码
import numpy as np
import pandas as pd

def test2():
    s = pd.Series(['Tom', 'William Rick', 'John', 'Alber@t', np.nan, '1234', 'SteveSmith'])
    print(s.str.len())
    print(s.str.lower())
    print(s.str.upper())


if __name__ == '__main__':
    test2()

7:自定义选项的设置

python 复制代码
import numpy as np
import pandas as pd

def test3():
    # 查看可以展示的最大行数和列数
    print(pd.get_option("display.max_rows"))
    print(pd.get_option("display.max_columns"))  # 默认显示的列数是0, 表示显示所有列
    print("=" * 40)
    # 设置最大行数和列数
    pd.set_option("display.max_rows", 10)
    pd.set_option("display.max_columns", 10)  # 调整成为最多显示10列
    print(pd.get_option("display.max_rows"))
    print(pd.get_option("display.max_columns"))
    print("=" * 40)
    # 恢复默认
    pd.reset_option("display.max_rows")
    pd.reset_option("display.max_columns")
    print(pd.get_option("display.max_rows"))
    print(pd.get_option("display.max_columns"))
    print("=" * 40)
    # 查看描述
    print(pd.describe_option("display.max_rows"))
    print(pd.describe_option("display.max_columns"))
    print("=" * 40)
    # 临时设置,只在with块中生效
    with pd.option_context("display.max_rows", 10, "display.max_columns", 10):
        print(pd.get_option("display.max_rows"))  # 10
        print(pd.get_option("display.max_columns"))  # 10
        print("=" * 40)
    print(pd.get_option("display.max_rows")) # 60, 不受到option_context的设置的影响
    print(pd.get_option("display.max_columns")) # 10
    print("=" * 40)

if __name__ == '__main__':
    test3()
复制代码
60
0
========================================
10
10
========================================
60
0
========================================
display.max_rows : int
    If max_rows is exceeded, switch to truncate view. Depending on
    `large_repr`, objects are either centrally truncated or printed as
    a summary view. 'None' value means unlimited.

    In case python/IPython is running in a terminal and `large_repr`
    equals 'truncate' this can be set to 0 and pandas will auto-detect
    the height of the terminal and print a truncated object which fits
    the screen height. The IPython notebook, IPython qtconsole, or
    IDLE do not run in a terminal and hence it is not possible to do
    correct auto-detection.
    [default: 60] [currently: 60]
None
display.max_columns : int
    If max_cols is exceeded, switch to truncate view. Depending on
    `large_repr`, objects are either centrally truncated or printed as
    a summary view. 'None' value means unlimited.

    In case python/IPython is running in a terminal and `large_repr`
    equals 'truncate' this can be set to 0 or None and pandas will auto-detect
    the width of the terminal and print a truncated object which fits
    the screen width. The IPython notebook, IPython qtconsole, or IDLE
    do not run in a terminal and hence it is not possible to do
    correct auto-detection and defaults to 20.
    [default: 0] [currently: 0]
None
========================================
10
10
========================================
60
0
========================================

8:索引和数据查询

Python和NumPy索引运算符"[]"和属性运算符"."。可以在各种用例中快速轻松地访问Pandas数据结构

Pandas现在支持两种类型的多轴索引:下表中提到了三种类型:

索引 说明
.loc() 基于标签
.iloc() 基于整数
8.1:loc

.loc() 具有多种访问方法,例如:

  • 一个标量标签
  • 标签列表
  • 切片对象
  • 布尔数组

loc 需要两个单/列表/范围运算符,以","分隔。第一个指示行,第二个指示列。

python 复制代码
import pandas as pd
import numpy as np

def test1():
    df = pd.DataFrame(np.random.randn(8, 4),
                      index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
                      columns=['A', 'B', 'C', 'D'])  # 定义一个8行4列的DataFrame, 索引为a-h, 列名为A-D
    print(df)
    print("筛选出每一行的A列")
    print(df['A'])
    print("=" * 30)
    print(df.loc[:, 'A'])
    print("=" * 30)
    print("筛选出所有的AC列")
    print(df.loc[:, ['A', 'C']])
    print("=" * 30)
    print("同时可以指定行和列, 例如输出a, c, f行的A, C列")
    print(df.loc[['a', 'c', 'f'], ['A', 'C']])
    print("=" * 30)
    print("可以为所有的列指定行的范围,使用:")
    print(df.loc['a':'c', ['A', 'C']])
    print("=" * 30)

if __name__ == '__main__':
    test1()
复制代码
          A         B         C         D
a  1.382366 -1.163601  0.176177  0.672256
b -0.282480 -0.967808  0.930259  0.316234
c -0.043770  1.639644 -0.505511  1.421071
d  0.660928  0.612623  0.292144  1.240187
e  1.449997 -0.061115  0.104454  0.194620
f -1.048595 -0.017617  0.250532 -1.888130
g  0.856651  0.994930 -0.220943  1.727685
h -0.482508 -0.440425 -0.317698 -0.555271
筛选出每一行的A列
a    1.382366
b   -0.282480
c   -0.043770
d    0.660928
e    1.449997
f   -1.048595
g    0.856651
h   -0.482508
Name: A, dtype: float64
==============================
a    1.382366
b   -0.282480
c   -0.043770
d    0.660928
e    1.449997
f   -1.048595
g    0.856651
h   -0.482508
Name: A, dtype: float64
==============================
筛选出所有的AC列
          A         C
a  1.382366  0.176177
b -0.282480  0.930259
c -0.043770 -0.505511
d  0.660928  0.292144
e  1.449997  0.104454
f -1.048595  0.250532
g  0.856651 -0.220943
h -0.482508 -0.317698
==============================
同时可以指定行和列, 例如输出a, c, f行的A, C列
          A         C
a  1.382366  0.176177
c -0.043770 -0.505511
f -1.048595  0.250532
==============================
可以为所有的列指定行的范围,使用:
          A         C
a  1.382366  0.176177
b -0.282480  0.930259
c -0.043770 -0.505511
==============================
8.2:iloc

同理,只不过这个索引切片变成了整数

python 复制代码
import pandas as pd
import numpy as np

def test2():
    df = pd.DataFrame(np.random.randn(8, 4),
                      index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
                      columns=['A', 'B', 'C', 'D'])
    print(df)
    print("=" * 30)
    print("df中的前4行是:")
    print(df.iloc[:4])
    print("=" * 30)
    print("df中的后4行是:")
    print(df.iloc[4:])
    print("=" * 30)
    print("df中的第2行和第4行是:")
    print(df.iloc[[1, 3]])
    print("=" * 30)
    print("df中的前4行的后两列是:")
    print(df.iloc[:4, 2:])
    print("=" * 30)
    print("df中的第2行和第4行,第2列和第4列是:")
    print(df.iloc[[1, 3], [1, 3]])
    print("=" * 30)

if __name__ == '__main__':
    test2()
复制代码
          A         B         C         D
a -0.343237 -0.155287 -0.713359  0.071921
b -0.165145  0.491577 -0.107699  1.016831
c -0.527483 -0.391398  0.700065  0.054806
d -2.306590  0.223241 -0.097838 -0.431856
e -1.398431 -0.267630  0.366289 -0.373675
f -0.446961 -1.622479 -0.319882 -0.999932
g  0.645857 -1.249595  0.513613 -0.140469
h  0.227319  0.543510  1.938501  1.312588
==============================
df中的前4行是:
          A         B         C         D
a -0.343237 -0.155287 -0.713359  0.071921
b -0.165145  0.491577 -0.107699  1.016831
c -0.527483 -0.391398  0.700065  0.054806
d -2.306590  0.223241 -0.097838 -0.431856
==============================
df中的后4行是:
          A         B         C         D
e -1.398431 -0.267630  0.366289 -0.373675
f -0.446961 -1.622479 -0.319882 -0.999932
g  0.645857 -1.249595  0.513613 -0.140469
h  0.227319  0.543510  1.938501  1.312588
==============================
df中的第2行和第4行是:
          A         B         C         D
b -0.165145  0.491577 -0.107699  1.016831
d -2.306590  0.223241 -0.097838 -0.431856
==============================
df中的前4行的后两列是:
          C         D
a -0.713359  0.071921
b -0.107699  1.016831
c  0.700065  0.054806
d -0.097838 -0.431856
==============================
df中的第2行和第4行,第2列和第4列是:
          B         D
b  0.491577  1.016831
d  0.223241 -0.431856
==============================

四:高级操作

1:缺失值处理

1.1:对于缺失值的表示
  • NaNfloat 类型的默认缺失标记(来自 NumPy)
  • Noneobject 类型的缺失标记(Python 内置)
  • NaT :时间类型(datetime64)的缺失值
1.2:对于缺失值的检测
python 复制代码
df.isna()       # 返回布尔矩阵(True=缺失)
df.notna()      # 返回布尔矩阵(False=缺失)
df.isnull()     # isna() 的别名(功能相同)

df.isna().sum()             # 每列缺失值数量
df.isna().sum().sum()       # 整个DataFrame缺失值总数
df.isna().mean() * 100      # 每列缺失值百分比
python 复制代码
import numpy as np
import pandas as pd

dict = {
    'name': ['Tom', 'Nick', 'John'],
    'age': [20, 21, 19],
    'city': ['New York', 'Paris', None]
}

df = pd.DataFrame(dict)

print(df)
print("=" * 50)
print("布尔矩阵,如果是缺失值,将返回True, 否则是False")
print(df.isna())
print("=" * 50)
print("每列缺失值百分比")
print(df.isna().mean() * 100)
1.3:对于缺失值的删除
python 复制代码
# 删除包含缺失值的行(默认)
df.dropna(axis=0)  

# 删除包含缺失值的列
df.dropna(axis=1)  

# 删除全为缺失值的行
df.dropna(how='all')  

# 删除在特定列有缺失的行
df.dropna(subset=['col1', 'col2'])  

# 保留至少有 n 个非缺失值的行
df.dropna(thresh=5)
1.4:对于缺失值的填充
python 复制代码
# 可以固定值填充
df.fillna(0)  # 所有缺失值填充为0
df.fillna({'col1': 0, 'col2': 'unknown'})  # 按列指定填充值

# 统计值填充
df.fillna(df.mean()) # 用各列均值填充
df['col1'].fillna(df['col1'].median(), inplace=True)  # 中位数填充(原地修改)

# 前后填充
df.fillna(method='ffill')      # 用前一个有效值填充(向前填充)
df.fillna(method='bfill')      # 用后一个有效值填充(向后填充)
df.fillna(method='ffill', limit=2)  # 最多向前填充2个

# 插值法填充
df.interpolate()               # 线性插值(默认)
df.interpolate(method='time')  # 时间索引插值
df.interpolate(method='polynomial', order=2)  # 二阶多项式插值
1.5:其他常用操作
python 复制代码
# 将数值列的NaN替换为None(转换类型)
df['col'] = df['col'].where(pd.notnull(df['col']), None)

# 按分组用组内均值填充
df.groupby('group_col').transform(lambda x: x.fillna(x.mean()))

# 将占位符(如 -999)识别为缺失值
df.replace(-999, np.nan, inplace=True)

2:统计、分组和连接

Pandas核心操作 统计计算 分组操作 数据连接 描述性统计 相关性分析 分布分析 累积计算 分组聚合 分组转换 分组过滤 分组迭代 Merge合并 Join连接 Concatenate拼接 数据比较 高级技巧 透视表 交叉表 时间重采样 性能优化

2.1:统计计算

基础统计函数

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

# 基本统计信息
print("描述性统计:")
print(df.describe())

# 特定统计计算
print("\n销售总额:", df['Sales'].sum())
print("平均成本:", df['Cost'].mean())
print("最大销售额:", df['Sales'].max())
print("销售额标准差:", df['Sales'].std())
print("非空值数量:", df['Sales'].count())

相关性的分析

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

# 添加利润列
df['Profit'] = df['Sales'] - df['Cost']

# 计算相关性矩阵
correlation = df[['Sales', 'Cost', 'Profit']].corr()
print("\n相关性矩阵:")
print(correlation)

# 单个相关性
print("\n销售与利润的相关性:", df['Sales'].corr(df['Profit']))

累积计算

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

# 累积计算
df['Cumulative_Sales'] = df['Sales'].cumsum()  # 累积求和, [250, 550, 730, 1130]
df['Running_Max'] = df['Sales'].cummax()  # 累积最大值, [250, 300, 300, 400]

print("\n累积统计:")
print(df[['Product', 'Sales', 'Cumulative_Sales', 'Running_Max']])()

print("\n累积统计:")
print(df[['Product', 'Sales', 'Cumulative_Sales', 'Running_Max']])

分布分析

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

# 值计数
print("\n类别分布:")
print(df['Category'].value_counts())

# 分位数
print("\n销售额的25%分位数:", df['Sales'].quantile(0.25))
print("销售额的50%分位数:", df['Sales'].quantile(0.5))

# 直方图分箱
print("\n销售额分布:")
print(pd.cut(df['Sales'], bins=3).value_counts())
2.2:分组操作

基础分组统计

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)
# 按类别分组并计算平均值
category_group = df.groupby('Category')
print("\n按类别分组平均值:")
print(category_group.mean(numeric_only=True))

# 多列分组
multi_group = df.groupby(['Category', 'Product'])
print("\n多级分组总和:")
print(multi_group.sum(numeric_only=True))

聚合函数

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)
# 应用多个聚合函数
agg_results = category_group.agg({
    'Sales': ['sum', 'mean', 'max'],
    'Cost': ['min', 'median']
})
print("\n多聚合函数结果:")
print(agg_results)

分组转换

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)
# 计算组内标准化值
df['Z-Score'] = category_group['Sales'].transform(
    lambda x: (x - x.mean()) / x.std()
)

# 填充组内平均值
df['Sales_Filled'] = category_group['Sales'].transform(
    lambda x: x.fillna(x.mean())
)

print("\n分组转换结果:")
print(df[['Product', 'Category', 'Sales', 'Z-Score', 'Sales_Filled']])

分组过滤

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

# 过滤销售总额超过400的组
filtered_groups = category_group.filter(lambda x: x['Sales'].sum() > 400)
print("\n销售总额超过400的类别:")
print(filtered_groups)

分组迭代

python 复制代码
import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Sales': [250, 300, 180, 400, np.nan],
    'Cost': [120, 150, 100, 220, 180],
    'Category': ['Electronics', 'Furniture', 'Electronics', 'Furniture', 'Office']
}
df = pd.DataFrame(data)

category_group = df.groupby('Category')

print("\n分组迭代:")
for name, group in category_group:
    print(f"\n类别: {name}")
    print(group[['Product', 'Sales']])
2.3:数据连接

合并 (Merge)

python 复制代码
# 创建第二个DataFrame
inventory = pd.DataFrame({
    'Product': ['A', 'B', 'C', 'F'],
    'Stock': [15, 8, 20, 12],
    'Warehouse': ['NY', 'CA', 'TX', 'FL']
})

# 内连接
inner_merge = pd.merge(df, inventory, on='Product', how='inner')
print("\n内连接结果:")
print(inner_merge)

# 左连接
left_merge = pd.merge(df, inventory, on='Product', how='left')
print("\n左连接结果:")
print(left_merge)

# 指定不同列名
prices = pd.DataFrame({
    'Item': ['A', 'B', 'C', 'D', 'E'],
    'Price': [99, 149, 79, 199, 129]
})
different_cols = pd.merge(df, prices, left_on='Product', right_on='Item')
print("\n不同列名连接:")
print(different_cols[['Product', 'Sales', 'Price']])

连接 (Join)

python 复制代码
# 设置索引
df_indexed = df.set_index('Product')
inventory_indexed = inventory.set_index('Product')

# 索引连接
joined = df_indexed.join(inventory_indexed, how='left')
print("\n索引连接结果:")
print(joined)

拼接 (Concatenate)

python 复制代码
# 创建新数据
new_products = pd.DataFrame({
    'Product': ['F', 'G'],
    'Sales': [320, 280],
    'Cost': [150, 130],
    'Category': ['Electronics', 'Office']
})

# 垂直拼接
vertical_concat = pd.concat([df, new_products], ignore_index=True)
print("\n垂直拼接结果:")
print(vertical_concat)

# 水平拼接
additional_info = pd.DataFrame({
    'Product': ['A', 'B', 'C', 'D', 'E'],
    'Rating': [4.5, 4.2, 4.8, 4.0, 4.6]
})
horizontal_concat = pd.concat([df, additional_info.set_index('Product')], axis=1)
print("\n水平拼接结果:")
print(horizontal_concat)

比较与组合

python 复制代码
# 比较数据集
comparison = df.compare(new_products)
print("\n数据集比较:")
print(comparison)

# 组合数据
combine_first = df.set_index('Product').combine_first(
    new_products.set_index('Product')
)
print("\n组合数据结果:")
print(combine_first)
2.4:高级技巧

数据透视表

python 复制代码
# 创建透视表
pivot_table = pd.pivot_table(
    df, 
    values='Sales', 
    index='Category', 
    columns=pd.cut(df['Cost'], bins=[0, 150, 250]), 
    aggfunc=['sum', 'mean'],
    fill_value=0
)
print("\n数据透视表:")
print(pivot_table)

交叉表

python 复制代码
# 创建交叉表
cross_tab = pd.crosstab(
    df['Category'], 
    pd.cut(df['Cost'], bins=[0, 150, 250]),
    values=df['Sales'], 
    aggfunc='mean'
)
print("\n交叉表:")
print(cross_tab)

分组时间序列重采样

python 复制代码
# 创建时间序列数据
date_rng = pd.date_range(start='2023-01-01', end='2023-01-10', freq='D')
time_series = pd.DataFrame({
    'Date': date_rng,
    'Sales': np.random.randint(100, 500, size=(len(date_rng))),
    'Category': np.random.choice(['Electronics', 'Furniture', 'Office'], size=len(date_rng))
})

# 按类别分组并重采样
time_series.set_index('Date', inplace=True)
resampled = time_series.groupby('Category').resample('3D').sum()
print("\n时间序列重采样:")
print(resampled)

3:日期相关操作

3.1:日期的创建和转换

创建时间戳

python 复制代码
ts = pd.Timestamp("2023-08-15 14:30:00")
print("单个时间戳", ts)

# 时间范围, periods -> 个数, freq -> 时间间隔,D-天,H-小时,M-分钟,S-秒
date_range = pd.date_range('2023-01-01', periods=5, freq='D')
print("\n日期范围:", date_range)

# 工作日范围(bdate)
bdate_range = pd.bdate_range('2023-08-01', periods=5)
print("\n工作日范围:", bdate_range)

转换数据类型

python 复制代码
# 从字符串转换
dates = ['2023-01-01', '2023-02-15', '2023-03-22']
df = pd.DataFrame({'date_str': dates})
df['date_dt'] = pd.to_datetime(df['date_str'])
print("\n字符串转日期:\n", df)

# 从不同格式转换
euro_dates = ['15/08/2023', '20/09/2023', '05/11/2023']
df['euro_date'] = pd.to_datetime(euro_dates, format='%d/%m/%Y')
print("\n欧洲格式日期:\n", df)

# 处理错误日期
mixed_dates = ['2023-01-01', 'invalid', '2023-03-22', '2023-02-30']
df = pd.DataFrame({'date': mixed_dates})
df['converted'] = pd.to_datetime(df['date'], errors='coerce')
print("\n错误处理转换:\n", df)
3.2:日期时间属性提取

dt.year/month/day/...

基本属性的提取

python 复制代码
# 创建时间序列数据
dates = pd.date_range('2023-01-01', periods=10, freq='D')
df = pd.DataFrame({'date': dates})

# 提取日期组件
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['dayofweek'] = df['date'].dt.dayofweek  # 周一=0, 周日=6
df['day_name'] = df['date'].dt.day_name()
df['is_weekend'] = df['date'].dt.dayofweek >= 5

print("\n日期属性提取:\n", df.head())

时间属性的提取

python 复制代码
# 创建带时间的数据
times = pd.date_range('2023-01-01 08:00', periods=5, freq='4H')
df = pd.DataFrame({'datetime': times})

# 提取时间组件
df['hour'] = df['datetime'].dt.hour
df['minute'] = df['datetime'].dt.minute
df['second'] = df['datetime'].dt.second
df['time'] = df['datetime'].dt.time
df['period'] = pd.cut(df['hour'], 
                     bins=[0, 6, 12, 18, 24], 
                     labels=['Night', 'Morning', 'Afternoon', 'Evening'])

print("\n时间属性提取:\n", df)
3.3:日期时间运算

时间差的计算

python 复制代码
# 创建时间差
td1 = pd.Timedelta(days=5, hours=3)
print("\n时间差:", td1)

# 时间运算
start = pd.Timestamp('2023-01-01')
end = start + pd.Timedelta(weeks=2)
print("开始日期:", start)
print("结束日期:", end)
print("时间差:", end - start)

# 序列运算
dates = pd.date_range('2023-01-01', periods=5, freq='2D')
df = pd.DataFrame({'date': dates})
df['next_date'] = df['date'] + pd.Timedelta(days=1)
df['days_diff'] = (df['next_date'] - df['date']).dt.days

print("\n时间差计算:\n", df)

日期的偏移

python 复制代码
from pandas.tseries.offsets import *

# 基本偏移
date = pd.Timestamp('2023-08-15')
print("\n原始日期:", date)
print("加3天:", date + DateOffset(days=3))
print("加2个月:", date + DateOffset(months=2))

# 工作日偏移
print("下个工作日:", date + BDay())
print("下个月第一个工作日:", date + BMonthBegin())

# 特殊偏移
print("季度末:", date + BQuarterEnd())
print("财年末(3月):", date + FY5253Quarter(quarter=4, startingMonth=3, variation="nearest"))
3.4:时间序列操作

重新采样

python 复制代码
# 创建时间序列数据
date_rng = pd.date_range('2023-01-01', '2023-01-10', freq='8H')
data = np.random.randint(0, 100, size=(len(date_rng)))
ts = pd.Series(data, index=date_rng)

# 降采样 (低频)
daily_mean = ts.resample('D').mean()
daily_max = ts.resample('D').max()
print("\n按天重采样(均值):\n", daily_mean)
print("\n按天重采样(最大值):\n", daily_max)

# 升采样 (高频)
upsampled = ts.resample('2H').asfreq()  # 插入NaN
ffilled = ts.resample('2H').ffill()     # 向前填充
print("\n升采样(插入NaN):\n", upsampled.head(10))
print("\n升采样(向前填充):\n", ffilled.head(10))

# OHLC重采样
ohlc = ts.resample('D').ohlc()
print("\nOHLC重采样:\n", ohlc)

滚动窗口的计算

python 复制代码
# 创建时间序列
data = np.random.randn(100)
index = pd.date_range('2023-01-01', periods=100, freq='D')
ts = pd.Series(data, index=index)

# 滚动计算
rolling_mean = ts.rolling(window=7).mean()
rolling_std = ts.rolling(window=14).std()
expanding_mean = ts.expanding().mean()

# 组合计算
df = pd.DataFrame({
    'value': ts,
    '7d_mean': rolling_mean,
    '14d_std': rolling_std,
    'exp_mean': expanding_mean
})
print("\n滚动窗口计算(前10行):\n", df.head(10))

# 带偏移的滚动窗口
offset_mean = ts.rolling(window='30D').mean()  # 30天滚动平均
print("\n时间偏移滚动窗口:\n", offset_mean.head(10))
3.5:日期索引操作

时间索引切片

python 复制代码
# 创建时间序列数据
data = np.random.randn(100)
index = pd.date_range('2023-01-01', periods=100, freq='D')
ts = pd.Series(data, index=index)

# 基础切片
print("1月数据:\n", ts['2023-01'])
print("\n1月15日至1月20日:\n", ts['2023-01-15':'2023-01-20'])

# 部分日期字符串切片
print("\n所有1月15日数据:\n", ts[ts.index.day == 15])

# 时间范围切片
print("\n1月1日到3月1日:\n", ts['2023-01-01':'2023-03-01'])

时间索引操作

python 复制代码
# 设置时间索引
df = pd.DataFrame({
    'value': np.random.randn(100),
    'date': pd.date_range('2023-01-01', periods=100, freq='D')
})
df.set_index('date', inplace=True)

# 按时间组件选择
print("所有周一数据:\n", df[df.index.dayofweek == 0].head())

# 按时间范围选择
print("\n工作时间数据(9AM-5PM):\n", df.between_time('09:00', '17:00'))

# 按日期类型选择
print("\n月初数据:\n", df[df.index.is_month_start])
print("\n季度末数据:\n", df[df.index.is_quarter_end])
3.6:其他常用操作

时区的处理

python 复制代码
# 创建时区无关时间
date_rng = pd.date_range('2023-01-01 00:00', periods=3, freq='8H')
ts = pd.Series(range(3), index=date_rng)

# 添加时区
ts_utc = ts.tz_localize('UTC')
print("\nUTC时间:\n", ts_utc)

# 时区转换
ts_ny = ts_utc.tz_convert('America/New_York')
print("\n纽约时间:\n", ts_ny)

# 处理夏令时
dst_range = pd.date_range('2023-03-12 00:00', periods=5, freq='H', tz='America/New_York')
print("\n夏令时转换:\n", dst_range)

节假日日历

python 复制代码
from pandas.tseries.holiday import USFederalHolidayCalendar

# 创建美国节假日日历
cal = USFederalHolidayCalendar()
holidays = cal.holidays(start='2023-01-01', end='2023-12-31')
print("2023年美国联邦节假日:\n", holidays)

# 工作日计算
bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
date = pd.Timestamp('2023-12-24')  # 圣诞节前
print("\n2023-12-24后的第一个工作日:", date + bday_us)

# 自定义节假日
class MyCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('My Birthday', month=8, day=15),
        Holiday('Company Founding', month=11, day=1)
    ]
    
my_cal = MyCalendar()
my_holidays = my_cal.holidays(start='2023-01-01', end='2023-12-31')
print("\n自定义节假日:\n", my_holidays)

时间序列分析

python 复制代码
# 创建带趋势和季节性的时间序列
np.random.seed(42)
t = np.arange(100)
trend = 0.1 * t
seasonality = 5 * np.sin(2 * np.pi * t / 30)
noise = np.random.normal(0, 1, 100)
data = trend + seasonality + noise

index = pd.date_range('2023-01-01', periods=100, freq='D')
ts = pd.Series(data, index=index)

# 分解时间序列
from statsmodels.tsa.seasonal import seasonal_decompose
decomposed = seasonal_decompose(ts, model='additive', period=30)

# 创建包含组件的DataFrame
df_decomposed = pd.DataFrame({
    'observed': decomposed.observed,
    'trend': decomposed.trend,
    'seasonal': decomposed.seasonal,
    'residual': decomposed.resid
})

print("\n时间序列分解(前5行):\n", df_decomposed.head())

时间序列的存储

python 复制代码
# 高效存储时间序列
df = pd.DataFrame({
    'timestamp': pd.date_range('2023-01-01', periods=1000, freq='T'),
    'value': np.random.randn(1000)
})

# 优化数据类型
df['timestamp'] = df['timestamp'].astype('datetime64[s]')  # 精确到秒
print("\n优化前内存:", df.memory_usage(deep=True).sum())
df['value'] = df['value'].astype('float32')
print("优化后内存:", df.memory_usage(deep=True).sum())

# 使用Period表示时间区间
df['period'] = df['timestamp'].dt.to_period('D')
print("\n周期表示:\n", df[['timestamp', 'period']].head())

大型的时间序列处理

python 复制代码
# 创建大型时间序列
size = 10**6  # 100万行
big_ts = pd.Series(
    np.random.randn(size),
    index=pd.date_range('2020-01-01', periods=size, freq='S')
)

# 高效重采样
%timeit big_ts.resample('1T').mean()  # 原始方法

# 使用Grouper优化
%timeit big_ts.groupby(pd.Grouper(freq='1T')).mean()

# 下采样优化
%timeit big_ts.loc[big_ts.index.min():big_ts.index.max():'5T']  # 步长选择
3.7:完整实际实例演示
python 复制代码
import numpy as np
import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from statsmodels.tsa.seasonal import seasonal_decompose

# 创建销售数据集
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-06-30', freq='D')
sales = np.random.randint(50, 200, size=len(dates))
weekday_effect = np.where(dates.dayofweek == 5, 1.5, 1)  # 周六增加50%
sales = (sales * weekday_effect).astype(int)

df = pd.DataFrame({
    'date': dates,
    'sales': sales,
    'returns': np.random.randint(0, 10, size=len(dates))
})

# 设置日期索引
df.set_index('date', inplace=True)

# 1. 月度销售分析
monthly_sales = df['sales'].resample('ME').sum()
monthly_avg = df['sales'].resample('ME').mean()

# 2. 周分析
df['weekday'] = df.index.day_name()
weekly_sales = df.groupby('weekday')['sales'].mean().reindex([
    'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
])

# 3. 滚动分析
df['7d_avg'] = df['sales'].rolling(window=7).mean()
df['7d_std'] = df['sales'].rolling(window=7).std()

# 4. 节假日分析
cal = USFederalHolidayCalendar()
holidays = cal.holidays(start='2023-01-01', end='2023-06-30')
holiday_sales = df[df.index.isin(holidays)]['sales'].mean()

# 5. 时间序列分解
decomposed = seasonal_decompose(df['sales'], model='additive', period=30)

print("月度销售总额:\n", monthly_sales)
print("\n周平均销售:\n", weekly_sales)
print("\n节假日平均销售额:", holiday_sales)
复制代码
月度销售总额:
 date
2023-01-31    4073
2023-02-28    3558
2023-03-31    3923
2023-04-30    3913
2023-05-31    4063
2023-06-30    4525
Freq: ME, Name: sales, dtype: int64

周平均销售:
 weekday
Monday       119.192308
Tuesday      114.692308
Wednesday    133.961538
Thursday     128.346154
Friday       128.038462
Saturday     178.920000
Sunday       128.923077
Name: sales, dtype: float64

节假日平均销售额: 131.4

4:数据分类

5:可视化和SQL操作