Griffin Chow / Pandas学习笔记

Created Wed, 05 Jun 2024 00:00:00 +0000 Modified Thu, 06 Nov 2025 04:24:59 +0000
3152 Words

1 安装

1.1.1 使用 pip 安装

pip --version                    # 查看 pip 版本,确认安装状态
pip install pandas               # 安装最新稳定版 Pandas
pip install --upgrade pandas     # 将已安装的 Pandas 升级到最新版本

# MySQL相关依赖
pip install pymysql              # MySQL数据库连接器
pip install sqlalchemy           # SQL工具包和对象关系映射

验证:

import pandas as pd              # 导入 pandas 库
print(pd.__version__)            # 输出当前安装的 pandas 版本号

# 验证MySQL连接
import pymysql
try:
    conn = pymysql.connect(host='localhost', user='root', password='password', database='test')
    print("MySQL连接成功!")
    conn.close()
except Exception as e:
    print(f"MySQL连接失败:{e}")

1.1.2 使用 conda 安装

conda create -n myenv python=3.8 # 创建名为 myenv 的虚拟环境并指定 Python 3.8
conda activate myenv             # 激活该虚拟环境
conda install pandas             # 在虚拟环境中安装 Pandas
conda install pymysql            # 安装MySQL连接器
conda install sqlalchemy         # 安装SQL工具包

2 数据结构

2.1 Series

2.1.1 创建

import pandas as pd                                      # 导入 pandas

data_list = [1,2,3,4,5]                                  # 普通 Python 列表
s_list = pd.Series(data_list)                           # 用列表创建 Series,自动索引 0..n-1
s_dict = pd.Series({'a':1,'b':2,'c':3})                 # 用字典创建 Series,键成为索引
s_custom = pd.Series(data_list,                         # 创建带自定义索引的 Series
                     index=['one','two','three','four','five'])  # 指定索引标签

2.1.2 访问

print(s_list[0])                     # 通过位置访问第一个元素
print(s_dict['b'])                   # 通过标签访问索引 'b' 对应的值
print(s_list[:2])                    # 切片访问前两个元素(位置 0、1)
print(s_custom[['one','three']])     # 通过标签列表选取多个元素

2.1.3 属性与方法

print(len(s_list),                   # Series 长度(元素个数)
      s_list.index,                  # Index 对象(索引集合)
      s_list.values)                 # 底层 ndarray 值

print(s_list.max(),                  # 最大值
      s_list.min(),                  # 最小值
      s_list.sum(),                  # 求和
      s_list.mean())                 # 平均值

2.2 DataFrame

2.2.1 创建

import numpy as np                                      # 导入 NumPy 用于数组

df_dict = pd.DataFrame({                                # 通过字典创建 DataFrame
    'Name':['Alice','Bob','Charlie'],                   # 列 'Name'
    'Age':[25,30,35],                                   # 列 'Age'
    'City':['New York','Los Angeles','Chicago']         # 列 'City'
})

arr = np.array([[1,'Alice',25],                         # 构造二维数组:每一行是一条记录
                [2,'Bob',30],
                [3,'Charlie',35]])

df_arr = pd.DataFrame(arr,                              # 用二维数组创建 DataFrame
                      columns=['ID','Name','Age'])      # 指定列名(所有元素为对象类型)

2.2.2 访问

print(df_dict.iloc[0,1])       # 按整数位置访问:第 0 行第 1 列('Bob')
print(df_dict.loc[1,'Name'])   # 按标签访问:索引标签 1 的行的 'Name' 列
print(df_dict.iloc[1])         # 取第 1 行的所有列(返回 Series)
print(df_dict['City'])         # 访问 'City' 列(返回 Series)

2.2.3 属性与方法

print(df_dict.shape,           # (行数, 列数)
      df_dict.columns,         # 列名 Index
      df_dict.index)           # 行索引 Index

print(df_dict.describe())      # 数值列的描述性统计(非数值列忽略)
print(df_dict.head())          # 前 5 行(默认)
print(df_dict.tail())          # 后 5 行(默认)

2.2.4 读取文件

df_csv   = pd.read_csv('data.csv')        # 从 CSV 文件读取
df_excel = pd.read_excel('data.xlsx')     # 从 Excel 文件读取(需要 openpyxl 或 xlrd)
df_json  = pd.read_json('data.json')      # 从 JSON 文件读取

3 数据输入与输出

3.1 读取 CSV

df = pd.read_csv('data.csv')              # 一次性读取整个 CSV 为 DataFrame
with open('data.csv','r') as f:           # 原始文件逐行读取
    for line in f:                        # 遍历每一行字符串
        print(line.strip())               # 去掉末尾换行并打印

3.2 写入 CSV

df.to_csv('output.csv', index=False)      # 写出到 CSV,不包含行索引
with open('output_row_by_row.csv','w') as f:          # 手工逐行方式写出
    for _, row in df.iterrows():                      # 逐行迭代 DataFrame(较慢)
        f.write(','.join(map(str,row.values))+'\n')   # 把一行的值连接成逗号分隔写入

3.3 读取 Excel

df_x = pd.read_excel('data.xlsx', sheet_name='Sheet1')  # 指定工作表读取
for _, row in df_x.iterrows():                          # 逐行遍历
    print(row['Column1'], row['Column2'])               # 按列名访问字段

3.4 写入 Excel

df_x.to_excel('output.xlsx', index=False, sheet_name='Results')  # 写入到新工作表

3.5 读取 JSON

df_j = pd.read_json('data.json')     # 结构化 JSON 转 DataFrame
import json                          # 导入标准库 json
with open('data.json') as f:         # 打开原始 JSON 文件
    for rec in json.load(f):         # 解析为 Python 对象后逐条遍历
        print(rec)                   # 每条记录(通常是 dict)

3.6 写入 JSON

df_j.to_json('output.json',          # 输出文件名
             orient='records',       # 每行一个对象
             lines=True)             # 生成按行分隔的 JSON(NDJSON)
with open('output_row_by_row.json','w') as f:     # 手工逐行写
    for _, row in df_j.iterrows():                # 行迭代
        f.write(json.dumps(row.to_dict())+'\n')   # 转 dict 后序列化再换行

3.7 从数据库读取

import pymysql                                           # 导入 pymysql
import pandas as pd

# 连接MySQL数据库
conn = pymysql.connect(
    host='localhost',                                    # 数据库主机地址
    user='username',                                     # 数据库用户名
    password='password',                                 # 数据库密码
    database='database_name',                            # 数据库名称
    charset='utf8mb4'                                    # 字符集
)

df_sql = pd.read_sql_query("SELECT * FROM tablename",    # 执行 SQL 查询转换为 DataFrame
                           conn)
for _, row in df_sql.iterrows():                         # 逐行处理结果
    print(row['column_name'])                            # 打印某一列
conn.close()                                             # 关闭连接

3.8 写入数据库

import pymysql
import pandas as pd

# 连接MySQL数据库
conn = pymysql.connect(
    host='localhost',                                    # 数据库主机地址
    user='username',                                     # 数据库用户名
    password='password',                                 # 数据库密码
    database='database_name',                            # 数据库名称
    charset='utf8mb4'                                    # 字符集
)

# 方法1:使用pandas的to_sql方法(需要sqlalchemy)
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://username:password@localhost/database_name?charset=utf8mb4')
df_sql.to_sql('tablename', engine,                      # 将 DataFrame 写入表
              if_exists='replace',                       # 若存在则替换
              index=False)                               # 不写入行索引

# 方法2:逐行写入(适合大数据量或需要自定义处理)
cursor = conn.cursor()
for _, row in df_sql.iterrows():                         # 逐行处理
    sql = "INSERT INTO tablename (col1, col2, col3) VALUES (%s, %s, %s)"
    cursor.execute(sql, (row['col1'], row['col2'], row['col3']))
conn.commit()                                            # 提交事务
cursor.close()                                           # 关闭游标
conn.close()                                             # 关闭连接

4 数据清洗

4.2 缺失值

df = pd.DataFrame({'A':[1,2,None,4],          # 列 A 有一个缺失
                   'B':[None,2,3,4],          # 列 B 有一个缺失
                   'C':[1,None,None,4]})      # 列 C 有两个缺失
print(df.isnull())                            # 每个元素是否缺失 True/False
print(df.isnull().sum())                      # 每列缺失值计数

填充:

print(df.fillna(df.mean()))                   # 用各列均值填充数值缺失(非数值列忽略)
print(df.fillna(0))                           # 所有缺失用 0 填充

删除:

print(df.dropna())                            # 删除含任意缺失值的整行
print(df.dropna(subset=['A']))                # 仅检查列 A,删除其中缺失的行

4.3 空值处理

df_clean = df.replace('', None)               # 将空字符串替换为 None(视为缺失处理)

4.4 数据格式转换

df_types = pd.DataFrame({'A':['1','2','3','4'],      # 数字但以字符串形式存储
                         'B':['5.5','6.1','7.3','8.2']})
df_types['A'] = df_types['A'].astype(int)            # 转换为整数类型
df_types['B'] = df_types['B'].astype(float)          # 转换为浮点数类型

日期:

df_dates = pd.DataFrame({'date_str':['2021-01-01','2021-02-01','2021-03-01']})
df_dates['date'] = pd.to_datetime(df_dates['date_str'])   # 字符串解析为 Timestamp

4.5 去重

df_dup = pd.DataFrame({'A':[1,1,2,3,4],           # A 列重复值 1
                       'B':['a','a','b','c','d']})
print(df_dup.drop_duplicates())                  # 删除完全重复行(保留首次出现)

4.6 异常值

out = pd.DataFrame({'A':[10,12,13,100,15]})      # 100 可能是异常值
mean, std = out['A'].mean(), out['A'].std()      # 计算均值与标准差
out['is_outlier'] = (abs(out['A']-mean) > 3*std) # 超过 3 标准差标记为异常
clean = out[~out['is_outlier']]                  # 过滤掉异常值的行

5 数据操作

5.1 选择与过滤

5.1.1 标签选择

df = pd.DataFrame({'Name':['Alice','Bob','Charlie','David'],    # 四行记录
                   'Age':[25,30,35,40],
                   'City':['New York','Los Angeles','Chicago','Houston']})
print(df['Age'])                         # 选取单列(Series)
print(df[['Name','Age']])                # 选取多列(DataFrame)
print(df.loc[1])                         # 按标签索引 1 行(第二行)
print(df.loc[1,'Age'])                   # 标签 1 行的 'Age' 值

5.1.2 位置选择

print(df.iat[0,0])                       # 按整数位置:第 0 行第 0 列元素
print(df.iloc[0:2,1:3])                  # 切片:行 0-1;列 1-2(不包含列 3)

5.1.3 布尔索引

print(df[df['Age']>30])                  # 过滤 Age > 30 的行
print(df[(df['Age']>30) &                 # 复合条件:Age > 30 且 City == 'Chicago'
          (df['City']=='Chicago')])

5.2 排序

print(df.sort_values('Age'))             # 按 Age 升序
print(df.sort_values('Age', ascending=False))  # 按 Age 降序
print(df.sort_values(['City','Age']))     # 先按 City,再按 Age 升序

5.3 分组与聚合

df_g = pd.DataFrame({'Name':['Alice','Bob','Charlie','David','Eve'],  # 五条记录
                     'Age':[25,30,35,25,30],
                     'City':['New York','Los Angeles','Chicago','New York','Chicago']})
grp = df_g.groupby('City')                     # 按 City 分组
print(grp['Age'].mean())                       # 每个城市 Age 平均值
print(grp.agg({'Age':['mean','max'],           # 多重聚合:平均值 + 最大值
               'Name':'count'}))               # Name 计数
print(df_g.groupby('Age').agg({'City':pd.Series.nunique,   # 每个年龄的不同城市数量
                               'Name':'count'}))           # 每个年龄的出现人数

5.4 合并与连接

5.4.1 merge

df1 = pd.DataFrame({'EmployeeID':[1,2,3,4],          # 员工基本信息
                    'Name':['Alice','Bob','Charlie','David']})
df2 = pd.DataFrame({'EmployeeID':[3,4,5,6],          # 薪资信息(部分重叠)
                    'Salary':[70000,80000,60000,75000]})
print(pd.merge(df1, df2, on='EmployeeID', how='inner'))  # 交集
print(pd.merge(df1, df2, on='EmployeeID', how='left'))   # 左表全部
print(pd.merge(df1, df2, on='EmployeeID', how='right'))  # 右表全部
print(pd.merge(df1, df2, on='EmployeeID', how='outer'))  # 并集

5.4.2 concat

df3 = pd.DataFrame({'Name':['Alice','Bob'],          # 第一批
                    'Age':[25,30]})
df4 = pd.DataFrame({'Name':['Charlie','David'],      # 第二批
                    'Age':[35,40]})
print(pd.concat([df3, df4], axis=0, ignore_index=True))  # 纵向拼接并重置索引
df5 = pd.DataFrame({'Salary':[70000,80000]})             # 单独薪资列
print(pd.concat([df3, df5], axis=1))                     # 横向拼接,按行对齐

5.4.3 join

df6 = pd.DataFrame({'EmployeeID':[1,2,3],
                    'Department':['HR','Finance','IT']}).set_index('EmployeeID')   # 设索引用于连接
df7 = pd.DataFrame({'EmployeeID':[1,2,4],
                    'Salary':[70000,80000,60000]}).set_index('EmployeeID')

print(df6.join(df7, how='inner'))       # 索引交集
print(df6.join(df7, how='left'))        # 左索引全部
print(df6.join(df7, how='right'))       # 右索引全部
print(df6.join(df7, how='outer'))       # 索引并集

6 数据分析

6.1 描述性统计

import pandas as pd                                    # 导入 pandas
df = pd.DataFrame({'A':[1,2,3,4,5],                    # 构造示例数据列 A
                   'B':[5,6,7,8,9]})                   # 构造示例数据列 B
print(df['A'].mean(), df['A'].median(), df['A'].var()) # 输出 A 列均值/中位数/方差
print(df['A'].quantile([0.25,0.5,0.75]))               # A 列 25%、50%、75% 分位数

6.2 相关性

corr_ab = df['A'].corr(df['B'])    # A 与 B 的皮尔逊相关系数
corr_matrix = df.corr()            # 所有数值列的相关性矩阵

6.3 数据透视表

pivot_df = pd.DataFrame({                              # 创建用于透视的 DataFrame
  'Name':['Alice','Bob','Charlie','David','Eve','Frank'],
  'City':['New York','New York','Chicago','Chicago','Los Angeles','Los Angeles'],
  'Sales':[100,150,200,300,250,400]
})
print(pivot_df.pivot_table(values='Sales', index='City', aggfunc='sum'))          # 每城市销售总和
print(pivot_df.pivot_table(values='Sales', index='City', columns='Name',          # 城市×姓名矩阵
                           aggfunc='sum', fill_value=0))                          # 缺失填 0

6.4 时间序列

date_range = pd.date_range('2021-01-01', periods=5, freq='D')  # 生成 5 天日期
ts = pd.DataFrame([1,3,5,7,9], index=date_range,               # 建立时间序列 DataFrame
                  columns=['Value'])
print(ts.resample('2D').sum())                                 # 以 2 天为频率重采样求和

趋势可视化:

import matplotlib.pyplot as plt            # 导入绘图库
plt.plot(ts.index, ts['Value'], marker='o')# 绘制时间序列折线图
plt.show()                                 # 显示图形

7 常用函数

7.2 字符串处理

sdf = pd.DataFrame({'Name':[' Alice ','  Bob ','Charlie',' David ']})  # 含空格的姓名
sdf['Name'] = sdf['Name'].str.strip().str.upper()                      # 去空格并转大写
print(sdf[sdf['Name'].str.contains('CHARLIE')])                        # 过滤包含 CHARLIE 的行

7.3 apply / map

df_apply = pd.DataFrame({'A':[1,2,3,4],'B':[10,20,30,40]})  # 示例数据
df_apply['A_plus_ten'] = df_apply['A'].apply(lambda x: x+10) # 对 A 列每个元素加 10

df_map = pd.DataFrame({'A':['apple','banana','cherry']})    # 水果单列
mapping = {'apple':'fruit_1','banana':'fruit_2','cherry':'fruit_3'}  # 映射字典
df_map['A'] = df_map['A'].map(mapping)                      # 映射替换

8 数据可视化

8.1 Matplotlib 基础

import matplotlib.pyplot as plt, pandas as pd          # 导入所需库
df_sales = pd.DataFrame({'Year':[2017,2018,2019,2020,2021], # 年份列
                         'Sales':[150,200,250,300,350]})   # 销售列
plt.bar(df_sales['Year'], df_sales['Sales'])           # 绘制柱状图
plt.show()                                             # 显示图形

df_line = pd.DataFrame({'Month':['Jan','Feb','Mar','Apr','May','Jun'], # 月份
                        'Temp':[30,32,35,28,25,30]})                   # 温度
plt.plot(df_line['Month'], df_line['Temp'], marker='o') # 绘制折线图
plt.show()

plt.scatter(df_sales['Year'], df_sales['Sales'])       # 绘制散点图
plt.show()

8.2 Seaborn

import seaborn as sns, numpy as np                     # 导入 seaborn 与 numpy
dist = np.random.normal(0,1,1000)                      # 生成正态分布数据
sns.histplot(dist, bins=30, kde=True)                  # 直方图 + KDE 曲线

df_box = pd.DataFrame({'Category':['A','A','A','B','B','B'], # 类别
                       'Value':[1,2,3,4,5,6]})
sns.boxplot(x='Category', y='Value', data=df_box)       # 绘制箱型图
sns.violinplot(x='Category', y='Value', data=df_box)    # 绘制小提琴图

9 性能优化

9.1 类型与内存

import pandas as pd
N=1_000_00                                             # 设定重复倍数 100000
df = pd.DataFrame({
  'A':[1,2,3,4,5]*N,                                   # 大量整数
  'B':[10.5,20.5,30.5,40.5,50.5]*N,                    # 大量浮点
  'C':['Cat1','Cat2','Cat3','Cat4','Cat5']*N           # 类别字符串
})
print(df.memory_usage(deep=True))                      # 查看深度内存占用
df['A']=df['A'].astype('int32')                        # 缩小整数类型
df['B']=df['B'].astype('float32')                      # 缩小浮点类型
df['C']=df['C'].astype('category')                     # 转换为分类类型
print(df.memory_usage(deep=True))                      # 再次查看内存占用
del df['C']                                            # 删除不再需要的列释放内存

9.2 大数据处理

# 分块读取 large_file.csv —— 示例:逐块计算 A 列均值
chunk_means=[]                                         # 存储各块均值
for chunk in pd.read_csv('large_file.csv', chunksize=100_000):  # 每块 10 万行
    chunk_means.append(chunk['A'].mean())              # 计算并存储当前块均值
overall_mean = sum(chunk_means)/len(chunk_means)       # 汇总整体均值
print(overall_mean)                                    # 输出结果

Dask:

import dask.dataframe as dd                            # 导入 Dask DataFrame
ddf = dd.read_csv('large_file.csv')                    # 延迟读取大文件
print(ddf['A'].mean().compute())                       # 触发实际计算

10 案例分析

10.1 清洗与描述

raw = pd.DataFrame({                                   # 构造原始数据集
 'Name':['Alice','Bob','Charlie','David','Eve','Frank',None,'Bob'], # 含缺失/重复
 'Age':[25,30,None,35,40,45,150,30],                   # 年龄列含缺失和异常值 150
 'City':['New York','Los Angeles','Chicago','Houston','Chicago',None,'New York','Los Angeles'],
 'Salary':[70000,80000,None,120000,95000,100000,110000,95000]
})
print(raw.isnull().sum())                              # 每列缺失值统计
raw['Age'].fillna(raw['Age'].mean(), inplace=True)     # 用年龄均值填充缺失
raw['City'].fillna('Unknown', inplace=True)            # 缺失城市填充 Unknown
raw['Salary'].fillna(raw['Salary'].median(), inplace=True) # 薪资缺失填中位数
raw = raw[raw['Age']<=100].drop_duplicates()           # 去掉异常年龄与重复行
print(raw.describe(include='all'))                     # 全部列描述统计

10.2 销售可视化

sales = pd.DataFrame({                                 # 月份+类别销售数据
 'Month':['Jan','Jan','Feb','Feb','Mar','Mar','Apr','Apr','May','May'],
 'Sales':[150,200,250,300,350,400,450,500,550,600],
 'Category':['A','B','A','B','A','B','A','B','A','B']
})
tot = sales.groupby('Category')['Sales'].sum()         # 按类别汇总总销售
best = tot.idxmax()                                    # 找到销售最高的类别
pivot = sales.pivot_table(values='Sales',              # 生成透视表:月×类别
                          index='Month',
                          columns='Category',
                          aggfunc='sum')

10.3 时间序列预测(ARIMA 简例)

from statsmodels.tsa.arima.model import ARIMA          # 导入 ARIMA 模型
date_idx = pd.date_range('2021-01-01', periods=24, freq='M') # 24 个月索引
series = pd.Series(                                    # 销售时间序列
 [100,120,130,150,170,180,190,200,210,220,230,250,
  260,270,280,300,310,320,330,350,360,370,380,400],
 index=date_idx)
model = ARIMA(series, order=(1,1,1)).fit()             # 拟合 ARIMA(1,1,1)
forecast = model.forecast(steps=6)                     # 预测未来 6 期

10.4 产品销售趋势

prod = pd.DataFrame({                                  # 产品月度销售
 'Product':['A','B','C','A','B','C','A','B','C'],
 'Sales':[100,150,200,130,170,220,140,180,250],
 'Month':['Jan','Jan','Jan','Feb','Feb','Feb','Mar','Mar','Mar']
})
total = prod.groupby('Product')['Sales'].sum()         # 按产品汇总总销售额
monthly = prod.groupby(['Month','Product'])['Sales']   # 月+产品分组
monthly = monthly.sum().unstack()                      # 转换成宽格式(列为产品)