📚 Pandas 完全教程 - 从入门到精通
📚 Pandas 完全教程 - 从入门到精通
Pandas是Python数据分析的核心库,提供了高效的数据结构和数据分析工具。本教程从原理到实践,全面讲解Pandas的使用。
第一部分:Pandas核心数据结构
1.1 Series - 一维数据
Series是什么?
- 带标签的一维数组
- 可以存储任何数据类型
- 类似字典 + 数组的结合体
import pandas as pd
import numpy as np
# ========== 创建Series ==========
# 从列表创建(自动生成索引)
s1 = pd.Series([1, 2, 3, 4, 5])
print("从列表创建:")
print(s1)
print(f"索引: {s1.index}")
print(f"值: {s1.values}")
print(f"数据类型: {s1.dtype}")
# 从字典创建(key作为索引)
s2 = pd.Series({
'张三': 85,
'李四': 92,
'王五': 78
})
print("\n从字典创建:")
print(s2)
# 指定索引
s3 = pd.Series(
[10, 20, 30],
index=['a', 'b', 'c']
)
print("\n指定索引:")
print(s3)
# ========== Series的索引访问 ==========
print("\n索引访问:")
print(f"s3['a'] = {s3['a']}")
print(f"s3[0] = {s3[0]}") # 位置索引
print(f"s3[['a', 'c']] = \n{s3[['a', 'c']]}") # 花式索引
# ========== Series的运算 ==========
print("\nSeries运算:")
print(f"s3 + 10:\n{s3 + 10}")
print(f"s3 * 2:\n{s3 * 2}")
print(f"s3 > 15:\n{s3 > 15}")
# ========== Series的统计方法 ==========
print("\n统计方法:")
print(f"平均值: {s2.mean()}")
print(f"最大值: {s2.max()}")
print(f"最小值: {s2.min()}")
print(f"标准差: {s2.std()}")
print(f"描述统计:\n{s2.describe()}")
Series的底层原理:
- Series是建立在NumPy数组之上的
- index和values是两个独立的数组
- 索引操作实际上是字典查找 + 数组访问的结合
1.2 DataFrame - 二维数据
DataFrame是什么?
- 表格型数据结构
- 每列可以是不同的数据类型
- 类似Excel表格或SQL表
# ========== 创建DataFrame ==========
# 方式1:从字典创建(列名作为key)
df1 = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 28, 22],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 9000, 7500]
})
print("从字典创建:")
print(df1)
# 方式2:从列表创建(指定列名)
df2 = pd.DataFrame(
[
['张三', 25, '北京', 8000],
['李四', 30, '上海', 12000],
['王五', 28, '广州', 9000],
],
columns=['姓名', '年龄', '城市', '工资']
)
print("\n从列表创建:")
print(df2)
# 方式3:从NumPy数组创建
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
df3 = pd.DataFrame(arr, columns=['A', 'B', 'C'])
print("\n从NumPy数组创建:")
print(df3)
# ========== DataFrame的基本属性 ==========
print("\nDataFrame属性:")
print(f"形状: {df1.shape}") # (行数, 列数)
print(f"行数: {len(df1)}")
print(f"列数: {df1.shape[1]}")
print(f"列名: {df1.columns.tolist()}")
print(f"索引: {df1.index.tolist()}")
print(f"数据类型:\n{df1.dtypes}")
# ========== 查看数据 ==========
print("\n查看数据:")
print(f"前2行:\n{df1.head(2)}")
print(f"后2行:\n{df1.tail(2)}")
print(f"基本信息:\n{df1.info()}") # 查看数据类型和缺失值
print(f"统计信息:\n{df1.describe()}") # 数值列的统计
第二部分:数据导入与导出
import pandas as pd
# ========== 从文件读取数据 ==========
# CSV文件读取
def load_csv_example():
"""读取CSV文件的多种方式"""
# 基本读取
df = pd.read_csv('data.csv')
# 指定分隔符
df = pd.read_csv('data.csv', sep='\t') # 制表符分隔
# 指定编码
df = pd.read_csv('data.csv', encoding='utf-8')
# 指定索引列
df = pd.read_csv('data.csv', index_col=0)
# 指定需要读取的列
df = pd.read_csv('data.csv', usecols=['姓名', '年龄'])
# 跳过行
df = pd.read_csv('data.csv', skiprows=[0, 2]) # 跳过第0和2行
# 处理缺失值
df = pd.read_csv('data.csv', na_values=['N/A', 'NULL'])
# 分块读取大文件
chunk_size = 10000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
# 处理每个块
process(chunk)
return df
# ========== Excel文件读取 ==========
def load_excel_example():
"""读取Excel文件的多种方式"""
# 基本读取
df = pd.read_excel('data.xlsx')
# 指定sheet
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
# 读取多个sheet
sheets = pd.read_excel('data.xlsx', sheet_name=['Sheet1', 'Sheet2'])
df1 = sheets['Sheet1']
df2 = sheets['Sheet2']
# 读取所有sheet
all_sheets = pd.read_excel('data.xlsx', sheet_name=None)
return df
# ========== JSON文件读取 ==========
def load_json_example():
"""读取JSON数据"""
# 读取JSON文件
df = pd.read_json('data.json')
# 读取JSON字符串
json_str = '{"name": ["A", "B"], "value": [1, 2]}'
df = pd.read_json(json_str)
# 处理嵌套JSON
df = pd.read_json('nested.json', orient='records')
return df
# ========== 数据库读取 ==========
def load_database_example():
"""从数据库读取数据"""
import sqlite3
# 创建数据库连接
conn = sqlite3.connect('database.db')
# 执行SQL查询
df = pd.read_sql_query(
"SELECT * FROM users WHERE age > 25",
conn
)
# 读取整个表
df = pd.read_sql_table('users', conn)
conn.close()
return df
# ========== 数据导出 ==========
def export_data_example(df):
"""导出数据到不同格式"""
# 导出为CSV
df.to_csv('output.csv', index=False, encoding='utf-8')
# 导出为Excel
df.to_excel('output.xlsx', sheet_name='数据', index=False)
# 导出为JSON
df.to_json('output.json', orient='records', force_ascii=False)
# 导出为HTML表格
df.to_html('output.html')
# 导出为SQL
import sqlite3
conn = sqlite3.connect('database.db')
df.to_sql('table_name', conn, if_exists='replace', index=False)
conn.close()
第三部分:数据选择与索引
import pandas as pd
import numpy as np
# 创建示例数据
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八'],
'年龄': [25, 30, 28, 22, 35, 27],
'城市': ['北京', '上海', '广州', '深圳', '北京', '上海'],
'工资': [8000, 12000, 9000, 7500, 15000, 10000],
'部门': ['技术', '销售', '技术', '人事', '销售', '技术'],
'入职日期': pd.date_range('2020-01-01', periods=6)
})
print("示例数据:")
print(df)
# ========== 列选择 ==========
print("\n===== 列选择 =====")
# 单列选择(返回Series)
print("单列选择:")
print(df['姓名'])
# 或
print(df.姓名) # 可以这样访问,但有局限性(列名不能有空格等)
# 多列选择(返回DataFrame)
print("\n多列选择:")
print(df[['姓名', '年龄', '工资']])
# 条件选择列(过滤列)
print("\n选择所有数值列:")
print(df.select_dtypes(include=[np.number]))
# ========== 行选择 ==========
print("\n===== 行选择 =====")
# 使用iloc(基于位置)
print("使用iloc:")
print(df.iloc[0]) # 第一行
print(df.iloc[0:3]) # 前三行
print(df.iloc[[0, 2, 4]]) # 第0,2,4行
# 使用loc(基于标签)
print("\n使用loc:")
print(df.loc[0]) # 索引为0的行
print(df.loc[0:2]) # 索引0到2的行
# 布尔索引(条件筛选)
print("\n布尔索引:")
print(df[df['年龄'] > 25]) # 年龄大于25
print(df[(df['年龄'] > 25) & (df['城市'] == '北京')]) # 多条件
print(df[df['城市'].isin(['北京', '上海'])]) # 城市是北京或上海
# ========== 高级索引 ==========
# query方法(类似SQL的WHERE)
print("\nquery方法:")
print(df.query('年龄 > 25 and 城市 == "北京"'))
# 字符串方法
print("\n字符串方法:")
print(df[df['姓名'].str.startswith('张')]) # 名字以张开头
# 正则表达式
print(df[df['姓名'].str.contains('三|五')]) # 名字包含三或五
# ========== 多级索引 ==========
def multi_index_example():
"""多级索引(MultiIndex)的创建和使用"""
# 创建多级索引
arrays = [
['A', 'A', 'B', 'B', 'C', 'C'],
['one', 'two', 'one', 'two', 'one', 'two']
]
index = pd.MultiIndex.from_arrays(arrays, names=['first', 'second'])
df_multi = pd.DataFrame(
np.random.randn(6, 3),
index=index,
columns=['col1', 'col2', 'col3']
)
print("多级索引DataFrame:")
print(df_multi)
# 访问多级索引
print("\n访问多级索引:")
print(df_multi.loc['A']) # 第一级
print(df_multi.loc[('A', 'one')]) # 完整的二级索引
print(df_multi.xs('one', level='second')) # 按level访问
return df_multi
# ========== 索引重置和设置 ==========
print("\n===== 索引操作 =====")
# 设置索引
df_set = df.set_index('姓名')
print("设置姓名列为索引:")
print(df_set)
# 重置索引
df_reset = df_set.reset_index()
print("\n重置索引:")
print(df_reset)
# 排序索引
df_sorted = df.sort_index(ascending=False)
print("\n按索引排序:")
print(df_sorted)
第四部分:数据清洗与预处理
import pandas as pd
import numpy as np
# ========== 处理缺失值 ==========
def handle_missing_values():
"""处理缺失值的完整方法"""
# 创建含缺失值的数据
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [6, np.nan, np.nan, 9, 10],
'C': [11, 12, 13, 14, 15]
})
print("原始数据(含缺失值):")
print(df)
# 检测缺失值
print("\n检测缺失值:")
print(df.isnull()) # True表示缺失
print(df.isna().sum()) # 每列缺失值数量
# 删除缺失值
print("\n删除缺失值:")
print(df.dropna()) # 删除任何含缺失值的行
print(df.dropna(how='all')) # 删除全是缺失值的行
print(df.dropna(thresh=2)) # 至少需要2个非缺失值
# 填充缺失值
print("\n填充缺失值:")
print(df.fillna(0)) # 填充为0
print(df.fillna(method='ffill')) # 向前填充
print(df.fillna(method='bfill')) # 向后填充
print(df.fillna(df.mean())) # 填充为平均值
# 插值填充
df_interp = df.copy()
df_interp['A'] = df_interp['A'].interpolate()
print("\n插值填充:")
print(df_interp)
return df
# ========== 处理重复值 ==========
def handle_duplicates():
"""处理重复数据"""
df = pd.DataFrame({
'姓名': ['张三', '李四', '张三', '王五', '李四'],
'年龄': [25, 30, 25, 28, 30],
'城市': ['北京', '上海', '北京', '广州', '上海']
})
print("原始数据(含重复):")
print(df)
# 检测重复
print("\n检测重复:")
print(df.duplicated()) # 标记重复的行
print(df.duplicated(subset=['姓名'])) # 检查指定列
print(f"重复行数: {df.duplicated().sum()}")
# 删除重复
print("\n删除重复:")
print(df.drop_duplicates()) # 删除完全重复的行
print(df.drop_duplicates(subset=['姓名'])) # 按姓名去重,保留第一个
print(df.drop_duplicates(subset=['姓名'], keep='last')) # 保留最后一个
return df
# ========== 数据类型转换 ==========
def type_conversion():
"""数据类型转换和优化"""
df = pd.DataFrame({
'字符串数字': ['1', '2', '3', '4'],
'文本': ['a', 'b', 'c', 'd'],
'小数': [1.1, 2.2, 3.3, 4.4],
'日期': ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04']
})
print("原始数据类型:")
print(df.dtypes)
# 转换数据类型
df['字符串数字'] = df['字符串数字'].astype(int)
df['日期'] = pd.to_datetime(df['日期'])
# 转换为category(节省内存)
df['文本'] = df['文本'].astype('category')
# 转换为数字(处理异常值)
df['可能有文本的数字'] = ['1', '2', 'N/A', '4']
df['可能有文本的数字'] = pd.to_numeric(
df['可能有文本的数字'],
errors='coerce' # 无法转换的变成NaN
)
print("\n转换后数据类型:")
print(df.dtypes)
print("\n数据:")
print(df)
# 内存优化
print(f"\n内存使用: {df.memory_usage(deep=True)}")
return df
# ========== 文本处理 ==========
def text_processing():
"""字符串处理和文本清洗"""
df = pd.DataFrame({
'姓名': [' 张三 ', '李四', '王五 ', ' 赵六'],
'邮箱': ['zhangsan@email.com', 'lisi@mail.com',
'wangwu@gmail.com', 'zhaoliu@work.com'],
'文本': ['Hello World!', 'Python Data', 'Pandas Tutorial',
'Data Analysis']
})
print("原始文本数据:")
print(df)
# 去除空格
df['姓名'] = df['姓名'].str.strip()
print("\n去除空格后的姓名:")
print(df['姓名'])
# 字符串方法
print("\n字符串方法:")
print(df['姓名'].str.upper()) # 大写
print(df['姓名'].str.len()) # 长度
# 分割字符串
df[['用户名', '域名']] = df['邮箱'].str.split('@', expand=True)
print("\n分割邮箱:")
print(df[['邮箱', '用户名', '域名']])
# 提取子串
df['域名后缀'] = df['域名'].str.extract(r'\.(\w+)$')
print("\n提取域名后缀:")
print(df[['域名', '域名后缀']])
# 条件替换
df['文本_替换'] = df['文本'].str.replace(' ', '_')
print("\n替换空格:")
print(df[['文本', '文本_替换']])
# 包含检查
print("\n是否包含Data:")
print(df['文本'].str.contains('Data'))
return df
# ========== 异常值处理 ==========
def outlier_detection():
"""检测和处理异常值"""
# 创建含异常值的数据
np.random.seed(42)
data = np.random.normal(100, 15, 1000)
data = np.append(data, [200, 250, 10, 5, 300]) # 添加异常值
df = pd.DataFrame({'值': data})
print("数据统计:")
print(df.describe())
# 方法1:Z-score
from scipy import stats
z_scores = np.abs(stats.zscore(df['值']))
outliers_zscore = df[z_scores > 3]
print(f"\nZ-score方法检测到的异常值数量: {len(outliers_zscore)}")
# 方法2:IQR(四分位距)
Q1 = df['值'].quantile(0.25)
Q3 = df['值'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = df[(df['值'] < lower_bound) | (df['值'] > upper_bound)]
print(f"IQR方法检测到的异常值数量: {len(outliers_iqr)}")
# 处理异常值(截断)
df_clean = df.copy()
df_clean['值'] = df_clean['值'].clip(lower_bound, upper_bound)
print(f"\n截断后统计:")
print(df_clean.describe())
return df, df_clean
第五部分:数据变换与计算
import pandas as pd
import numpy as np
# ========== 新增和修改列 ==========
def column_operations():
"""列的创建、修改和删除"""
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'基本工资': [8000, 12000, 9000, 7500],
'绩效奖金': [2000, 3000, 1500, 2500]
})
print("原始数据:")
print(df)
# 新增列(基于其他列计算)
df['总工资'] = df['基本工资'] + df['绩效奖金']
df['月平均工资'] = df['总工资'] / 12
print("\n新增计算列:")
print(df)
# 条件新增列
df['级别'] = df['总工资'].apply(
lambda x: '高级' if x > 12000 else '中级' if x > 9000 else '初级'
)
print("\n条件列:")
print(df)
# 使用np.where(高效向量化)
df['高绩效'] = np.where(df['绩效奖金'] > 2000, '是', '否')
print("\n向量化条件:")
print(df)
# 使用map进行映射
level_map = {'初级': 1, '中级': 2, '高级': 3}
df['级别代码'] = df['级别'].map(level_map)
print("\n映射列:")
print(df)
# 删除列
df_dropped = df.drop(['月平均工资', '级别代码'], axis=1)
print("\n删除列后:")
print(df_dropped)
return df
# ========== 数据变换 ==========
def data_transformation():
"""数据变换方法"""
df = pd.DataFrame({
'产品': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'],
'年份': [2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022],
'销售额': [100, 150, 200, 80, 120, 180, 90, 110, 130]
})
print("原始数据(长格式):")
print(df)
# 透视表(长格式转宽格式)
pivot = df.pivot(index='年份', columns='产品', values='销售额')
print("\n透视表(宽格式):")
print(pivot)
# 重置透视表(宽格式转长格式)
melt = pd.melt(pivot.reset_index(),
id_vars=['年份'],
var_name='产品',
value_name='销售额')
print("\n熔融数据(长格式):")
print(melt)
# 交叉表(频率统计)
cross = pd.crosstab(df['产品'], df['年份'])
print("\n交叉表:")
print(cross)
return df, pivot
# ========== 标准化和归一化 ==========
def scaling_example():
"""数据标准化和归一化"""
from sklearn.preprocessing import StandardScaler, MinMaxScaler
df = pd.DataFrame({
'特征A': np.random.randn(100) * 100,
'特征B': np.random.randn(100) * 10,
'特征C': np.random.randn(100) * 1000
})
print("原始数据统计:")
print(df.describe())
# Z-score标准化(均值为0,标准差为1)
scaler_standard = StandardScaler()
df_standard = pd.DataFrame(
scaler_standard.fit_transform(df),
columns=df.columns
)
print("\n标准化后统计:")
print(df_standard.describe())
# Min-Max归一化(范围为0-1)
scaler_minmax = MinMaxScaler()
df_minmax = pd.DataFrame(
scaler_minmax.fit_transform(df),
columns=df.columns
)
print("\n归一化后统计:")
print(df_minmax.describe())
return df_standard, df_minmax
# ========== 自定义函数应用 ==========
def apply_functions():
"""apply、map、applymap的使用"""
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [6, 7, 8, 9, 10],
'C': [11, 12, 13, 14, 15]
})
print("原始数据:")
print(df)
# apply:对行或列应用函数
print("\napply应用:")
print(df.apply(lambda x: x.mean())) # 每列求平均
print(df.apply(lambda x: x.sum(), axis=1)) # 每行求和
# map:对Series的元素应用函数
print("\nmap应用:")
print(df['A'].map(lambda x: x ** 2))
# applymap:对DataFrame的每个元素应用函数
print("\napplymap应用:")
print(df.applymap(lambda x: x * 2))
# 使用自定义函数
def custom_func(row):
"""计算行的加权和"""
return row['A'] * 0.3 + row['B'] * 0.5 + row['C'] * 0.2
df['加权和'] = df.apply(custom_func, axis=1)
print("\n自定义函数应用:")
print(df)
return df
# ========== 窗口函数和滚动计算 ==========
def rolling_functions():
"""滚动计算和窗口函数"""
# 创建时间序列数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
df = pd.DataFrame({
'date': dates,
'price': 100 + np.cumsum(np.random.randn(100) * 2)
})
df.set_index('date', inplace=True)
print("原始价格数据:")
print(df.head(10))
# 移动平均
df['MA_7'] = df['price'].rolling(window=7).mean()
df['MA_30'] = df['price'].rolling(window=30).mean()
# 移动标准差
df['std_7'] = df['price'].rolling(window=7).std()
# 指数加权移动平均(EWMA)
df['EWMA'] = df['price'].ewm(span=20).mean()
# 累积计算
df['cum_max'] = df['price'].cummax()
df['cum_min'] = df['price'].cummin()
# 分组滚动计算
df_by_group = pd.DataFrame({
'group': ['A']*50 + ['B']*50,
'value': np.random.randn(100) * 10
})
df_by_group['group_rolling_mean'] = df_by_group.groupby('group')['value'].transform(
lambda x: x.rolling(5, min_periods=1).mean()
)
print("\n分组滚动平均:")
print(df_by_group.head(10))
return df
第六部分:数据聚合与分组
import pandas as pd
import numpy as np
# ========== 基础聚合 ==========
def basic_aggregation():
"""数据聚合的多种方式"""
df = pd.DataFrame({
'部门': ['技术', '销售', '技术', '人事', '销售', '技术'],
'姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八'],
'工资': [8000, 12000, 9000, 7500, 15000, 10000],
'年龄': [25, 30, 28, 22, 35, 27]
})
print("原始数据:")
print(df)
# 分组
grouped = df.groupby('部门')
# 基本聚合
print("\n分组聚合:")
print(grouped['工资'].mean()) # 每个部门的平均工资
print(grouped['工资'].sum()) # 每个部门的工资总和
print(grouped['工资'].agg(['mean', 'sum', 'count'])) # 多个聚合
# 分组多个列的不同聚合
print("\n多列聚合:")
print(grouped.agg({
'工资': ['mean', 'sum', 'max'],
'年龄': ['mean', 'min', 'max']
}))
# 自定义聚合函数
def range_func(x):
return x.max() - x.min()
print("\n自定义聚合:")
print(grouped['工资'].agg(['mean', range_func]))
return grouped
# ========== 高级分组操作 ==========
def advanced_grouping():
"""高级分组操作"""
np.random.seed(42)
df = pd.DataFrame({
'部门': np.random.choice(['技术', '销售', '人事', '财务'], 100),
'城市': np.random.choice(['北京', '上海', '广州', '深圳'], 100),
'工资': np.random.normal(10000, 2000, 100),
'绩效': np.random.normal(0.8, 0.2, 100)
})
df['工资'] = df['工资'].clip(5000, 20000)
df['绩效'] = df['绩效'].clip(0.3, 1.2)
print("示例数据:")
print(df.head())
# transform:将聚合结果广播到原数据
df['部门平均工资'] = df.groupby('部门')['工资'].transform('mean')
df['相对工资'] = df['工资'] / df['部门平均工资']
print("\ntransform示例:")
print(df[['部门', '工资', '部门平均工资', '相对工资']].head())
# 分组过滤
# 保留平均工资大于10000的部门
filtered = df.groupby('部门').filter(lambda x: x['工资'].mean() > 10000)
print(f"\n过滤后数据行数: {len(filtered)}")
print(f"保留的部门: {filtered['部门'].unique()}")
# 分组应用自定义函数
def custom_agg(group):
return pd.Series({
'平均工资': group['工资'].mean(),
'工资标准差': group['工资'].std(),
'绩效平均': group['绩效'].mean(),
'人数': len(group)
})
result = df.groupby('部门').apply(custom_agg)
print("\n自定义聚合结果:")
print(result)
# 多级分组
multi_group = df.groupby(['部门', '城市'])
print("\n多级分组统计:")
print(multi_group['工资'].agg(['mean', 'count']).head())
return df
# ========== 数据合并 ==========
def data_merging():
"""数据合并的多种方式"""
# 创建示例数据
df1 = pd.DataFrame({
'ID': [1, 2, 3, 4, 5],
'姓名': ['张三', '李四', '王五', '赵六', '孙七'],
'部门': ['技术', '销售', '技术', '人事', '销售']
})
df2 = pd.DataFrame({
'ID': [1, 2, 3, 6, 7],
'工资': [8000, 12000, 9000, 7500, 15000],
'年龄': [25, 30, 28, 22, 35]
})
df3 = pd.DataFrame({
'ID': [1, 2, 3, 4, 5],
'城市': ['北京', '上海', '广州', '深圳', '北京'],
'入职日期': pd.date_range('2020-01-01', periods=5)
})
print("df1:")
print(df1)
print("\ndf2:")
print(df2)
print("\ndf3:")
print(df3)
# 内连接(只保留匹配的行)
merged_inner = pd.merge(df1, df2, on='ID', how='inner')
print("\n内连接结果:")
print(merged_inner)
# 左连接(保留左表所有行)
merged_left = pd.merge(df1, df2, on='ID', how='left')
print("\n左连接结果:")
print(merged_left)
# 右连接(保留右表所有行)
merged_right = pd.merge(df1, df2, on='ID', how='right')
print("\n右连接结果:")
print(merged_right)
# 外连接(保留所有行)
merged_outer = pd.merge(df1, df2, on='ID', how='outer')
print("\n外连接结果:")
print(merged_outer)
# 多表连接
merged_multi = pd.merge(
pd.merge(df1, df2, on='ID', how='left'),
df3, on='ID', how='left'
)
print("\n多表连接结果:")
print(merged_multi)
# concat:上下拼接
df_extra = pd.DataFrame({
'ID': [8, 9, 10],
'姓名': ['周八', '吴九', '郑十'],
'部门': ['财务', '技术', '销售']
})
concat_df = pd.concat([df1, df_extra], ignore_index=True)
print("\nconcat拼接:")
print(concat_df)
return merged_inner
# ========== 数据透视表 ==========
def pivot_table_example():
"""数据透视表详解"""
np.random.seed(42)
df = pd.DataFrame({
'日期': pd.date_range('2024-01-01', periods=100, freq='D'),
'产品': np.random.choice(['A', 'B', 'C', 'D'], 100),
'区域': np.random.choice(['北区', '南区', '东区', '西区'], 100),
'销售额': np.random.normal(100, 30, 100),
'数量': np.random.randint(1, 20, 100)
})
df['销售额'] = df['销售额'].clip(20)
print("示例数据:")
print(df.head())
# 基本透视表
pivot1 = pd.pivot_table(
df,
values='销售额',
index='产品',
columns='区域',
aggfunc='mean'
)
print("\n按产品和区域的平均销售额:")
print(pivot1)
# 多值聚合
pivot2 = pd.pivot_table(
df,
values=['销售额', '数量'],
index='产品',
columns='区域',
aggfunc=['sum', 'mean']
)
print("\n多值聚合:")
print(pivot2)
# 带边际和填充值
pivot3 = pd.pivot_table(
df,
values='销售额',
index='产品',
columns='区域',
aggfunc='sum',
fill_value=0,
margins=True,
margins_name='总计'
)
print("\n带边际的透视表:")
print(pivot3)
return pivot1
第七部分:时间序列分析
import pandas as pd
import numpy as np
# ========== 创建时间序列 ==========
def time_series_creation():
"""创建时间序列的各种方法"""
# 创建日期范围
dates = pd.date_range(
start='2024-01-01',
end='2024-12-31',
freq='D' # D:每日, H:每小时, M:月末, MS:月初
)
print("日期范围:")
print(f"长度: {len(dates)}")
print(dates[:10])
# 多种频率
print("\n不同频率:")
print(f"每小时: {pd.date_range('2024-01-01', periods=5, freq='H')}")
print(f"每个工作日: {pd.date_range('2024-01-01', periods=5, freq='B')}")
print(f"每个月: {pd.date_range('2024-01-01', periods=5, freq='M')}")
# 创建时间序列数据
np.random.seed(42)
df = pd.DataFrame({
'date': dates,
'value': np.cumsum(np.random.randn(len(dates)))
})
df.set_index('date', inplace=True)
print("\n时间序列数据:")
print(df.head())
return df
# ========== 时间序列重采样 ==========
def resampling_example():
"""时间序列重采样"""
# 创建高频数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=1000, freq='H')
df = pd.DataFrame({
'timestamp': dates,
'value': np.random.randn(len(dates)) * 10 + 100
})
df.set_index('timestamp', inplace=True)
print("原始数据(小时级):")
print(df.head())
# 降采样(从小时到天)
daily = df.resample('D').mean()
print("\n日平均:")
print(daily.head())
# 多种聚合
daily_agg = df.resample('D').agg({
'value': ['mean', 'max', 'min', 'std']
})
print("\n日聚合统计:")
print(daily_agg.head())
# 升采样(从日到小时)
df_daily = df.resample('D').mean()
df_hourly = df_daily.resample('H').ffill() # 向前填充
print(f"\n升采样后行数: {len(df_hourly)}")
# OHLC(开盘-最高-最低-收盘)
ohlc = df.resample('D')['value'].ohlc()
print("\nOHLC数据:")
print(ohlc.head())
return df
# ========== 时间序列移动和滞后 ==========
def lag_and_shift():
"""滞后和移动操作"""
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
df = pd.DataFrame({
'date': dates,
'price': 100 + np.cumsum(np.random.randn(100) * 1)
})
df.set_index('date', inplace=True)
print("原始数据:")
print(df.head(10))
# shift:数据移动
df['lag_1'] = df['price'].shift(1) # 滞后1期
df['lead_1'] = df['price'].shift(-1) # 超前1期
# diff:差分
df['diff_1'] = df['price'].diff(1) # 一阶差分
df['pct_change'] = df['price'].pct_change() * 100 # 百分比变化
# rolling:滚动窗口
df['ma_7'] = df['price'].rolling(7).mean()
df['ma_30'] = df['price'].rolling(30).mean()
# 指数加权
df['ewma'] = df['price'].ewm(span=20).mean()
print("\n变换后数据:")
print(df.tail(10))
return df
# ========== 时间序列日期操作 ==========
def datetime_operations():
"""日期和时间操作"""
df = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=1000, freq='H')
})
df['value'] = np.random.randn(1000)
# 提取时间成分
df['year'] = df['timestamp'].dt.year
df['month'] = df['timestamp'].dt.month
df['day'] = df['timestamp'].dt.day
df['hour'] = df['timestamp'].dt.hour
df['dayofweek'] = df['timestamp'].dt.dayofweek # 0=周一
df['is_weekend'] = df['timestamp'].dt.dayofweek >= 5
print("时间成分提取:")
print(df.head())
# 按时间筛选
df_2024 = df[df['timestamp'].dt.year == 2024]
df_q1 = df[df['timestamp'].dt.quarter == 1]
print(f"\n2024年数据量: {len(df_2024)}")
print(f"第一季度数据量: {len(df_q1)}")
# 时间范围筛选
df_filtered = df[(df['timestamp'] >= '2024-01-15') &
(df['timestamp'] <= '2024-01-20')]
print("\n特定时间范围数据:")
print(df_filtered.head())
return df
# ========== 时间序列完整分析 ==========
def complete_time_series_analysis():
"""完整的时间序列分析流程"""
# 创建数据
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=365*2, freq='D')
# 模拟包含趋势、季节性和噪声的数据
trend = np.linspace(100, 200, len(dates))
seasonal = 20 * np.sin(2 * np.pi * np.arange(len(dates)) / 365)
noise = np.random.randn(len(dates)) * 10
df = pd.DataFrame({
'date': dates,
'sales': trend + seasonal + noise
})
df.set_index('date', inplace=True)
print("时间序列数据:")
print(df.describe())
# 1. 分解时间序列
from statsmodels.tsa.seasonal import seasonal_decompose
decomposition = seasonal_decompose(df['sales'], model='additive', period=365)
df['trend'] = decomposition.trend
df['seasonal'] = decomposition.seasonal
df['residual'] = decomposition.resid
print("\n分解结果:")
print(df.head())
# 2. 计算统计指标
df['rolling_mean'] = df['sales'].rolling(30).mean()
df['rolling_std'] = df['sales'].rolling(30).std()
# 3. 检测异常值(3倍标准差)
df['zscore'] = (df['sales'] - df['sales'].rolling(30).mean()) / df['sales'].rolling(30).std()
df['is_anomaly'] = df['zscore'].abs() > 3
anomaly_count = df['is_anomaly'].sum()
print(f"\n异常值数量: {anomaly_count}")
print(f"异常值占比: {anomaly_count/len(df)*100:.2f}%")
# 4. 按周/月/季度汇总
weekly = df.resample('W').agg({'sales': ['mean', 'sum', 'std']})
monthly = df.resample('M').agg({'sales': ['mean', 'sum']})
quarterly = df.resample('Q').agg({'sales': ['mean', 'sum']})
print("\n月度统计:")
print(monthly.head())
return df, weekly, monthly
第八部分:实战案例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# ========== 案例1:销售数据分析 ==========
def sales_analysis_case():
"""完整的销售数据分析案例"""
# 生成销售数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=365, freq='D')
products = ['A', 'B', 'C', 'D', 'E']
regions = ['北区', '南区', '东区', '西区']
channels = ['线上', '线下']
# 随机生成销售数据
data = []
for date in dates:
for product in products:
for region in regions:
for channel in channels:
# 不同维度有不同的销售特征
base = {
'日期': date,
'产品': product,
'区域': region,
'渠道': channel,
'销售额': np.random.normal(
100 * (1 + np.random.rand() * 2),
30
),
'销量': np.random.randint(1, 50)
}
data.append(base)
df = pd.DataFrame(data)
df['销售额'] = df['销售额'].clip(10)
print("销售数据:")
print(df.head())
print(f"总数据量: {len(df)}")
# 1. 按产品分析
product_sales = df.groupby('产品')['销售额'].agg(['sum', 'mean', 'count']).round(2)
print("\n产品维度的销售额:")
print(product_sales)
# 2. 按区域分析
region_sales = df.groupby('区域')['销售额'].agg(['sum', 'mean']).round(2)
print("\n区域维度的销售额:")
print(region_sales)
# 3. 按渠道分析
channel_sales = df.groupby('渠道')['销售额'].agg(['sum', 'mean']).round(2)
print("\n渠道维度的销售额:")
print(channel_sales)
# 4. 时间趋势(按周汇总)
df['周'] = df['日期'].dt.isocalendar().week
weekly_sales = df.groupby('周')['销售额'].sum().reset_index()
print("\n每周销售趋势:")
print(weekly_sales.head(10))
# 5. 透视表:产品 × 区域
pivot = pd.pivot_table(
df,
values='销售额',
index='产品',
columns='区域',
aggfunc='sum',
fill_value=0
)
print("\n产品-区域透视表:")
print(pivot)
# 6. 找出Top产品
top_products = df.groupby('产品')['销售额'].sum().sort_values(ascending=False)
print(f"\nTop 3 产品: {top_products.head(3).index.tolist()}")
# 7. 销售额分布分析
sales_stats = df['销售额'].describe()
print(f"\n销售额统计:\n{sales_stats}")
# 8. 异常高销售记录
high_sales = df[df['销售额'] > df['销售额'].quantile(0.95)]
print(f"\n高销售记录 (Top 5%): {len(high_sales)}条")
return df, product_sales, pivot
# ========== 案例2:用户行为分析 ==========
def user_behavior_analysis():
"""用户行为分析案例"""
np.random.seed(42)
# 生成用户行为数据
n_users = 1000
n_events = 10000
users = [f'user_{i}' for i in range(n_users)]
actions = ['浏览', '收藏', '加入购物车', '下单', '支付']
pages = ['首页', '商品页', '购物车', '个人中心', '订单页']
data = {
'用户': np.random.choice(users, n_events),
'时间': pd.date_range('2024-01-01', periods=n_events, freq='5min'),
'行为': np.random.choice(actions, n_events, p=[0.4, 0.2, 0.15, 0.15, 0.1]),
'页面': np.random.choice(pages, n_events),
'停留时间(秒)': np.random.exponential(60, n_events).astype(int)
}
df = pd.DataFrame(data)
df['停留时间(秒)'] = df['停留时间(秒)'].clip(1, 300)
df['日期'] = df['时间'].dt.date
print("用户行为数据:")
print(df.head())
print(f"总事件数: {len(df)}")
# 1. 行为分布
action_dist = df['行为'].value_counts()
print("\n行为分布:")
print(action_dist)
# 2. 漏斗分析
funnel = df.groupby('行为').size()
funnel_pct = (funnel / funnel.iloc[0] * 100).round(2)
print("\n漏斗转化率 (%):")
for action, pct in funnel_pct.items():
print(f" {action}: {pct}%")
# 3. 用户活跃度
user_activity = df.groupby('用户').agg({
'行为': 'count',
'停留时间(秒)': 'mean'
}).rename(columns={'行为': '事件数'})
print("\n用户活跃度统计:")
print(user_activity['事件数'].describe())
# 4. 高频用户
active_users = user_activity.nlargest(10, '事件数')
print("\nTop 10 活跃用户:")
print(active_users)
# 5. 页面访问分析
page_stats = df.groupby('页面').agg({
'停留时间(秒)': ['mean', 'max', 'count']
})
print("\n页面访问统计:")
print(page_stats)
# 6. 时间模式(按小时)
df['小时'] = df['时间'].dt.hour
hourly_activity = df.groupby('小时').size()
print("\n小时活跃度:")
print(hourly_activity.head(10))
# 7. 用户留存初步分析
user_first = df.groupby('用户')['日期'].min()
user_last = df.groupby('用户')['日期'].max()
user_retention = (user_last - user_first).dt.days
print(f"\n用户平均活跃天数: {user_retention.mean():.2f}")
print(f"用户最长活跃天数: {user_retention.max()}")
return df, user_activity, funnel
# ========== 案例3:股票数据分析 ==========
def stock_analysis_case():
"""股票数据分析案例"""
np.random.seed(42)
# 生成模拟股票数据
dates = pd.date_range('2020-01-01', periods=1000, freq='D')
# 使用几何布朗运动模拟股价
mu = 0.001 # 日收益率
sigma = 0.02 # 波动率
returns = np.random.normal(mu, sigma, len(dates))
prices = 100 * np.exp(np.cumsum(returns))
# 生成OHLC数据
df = pd.DataFrame({
'日期': dates,
'开盘': prices * (1 + np.random.normal(0, 0.002, len(dates))),
'最高': prices * (1 + np.abs(np.random.normal(0, 0.005, len(dates)))),
'最低': prices * (1 - np.abs(np.random.normal(0, 0.005, len(dates)))),
'收盘': prices,
'成交量': np.random.randint(10000, 1000000, len(dates))
})
df.set_index('日期', inplace=True)
# 确保最高 >= 最低 >= 开盘/收盘
df['最高'] = df[['开盘', '收盘', '最高']].max(axis=1)
df['最低'] = df[['开盘', '收盘', '最低']].min(axis=1)
print("股票数据:")
print(df.head())
print(f"数据量: {len(df)}")
# 1. 收益率计算
df['日收益率'] = df['收盘'].pct_change() * 100
df['累计收益率'] = (df['收盘'] / df['收盘'].iloc[0] - 1) * 100
# 2. 移动平均线
df['MA_20'] = df['收盘'].rolling(20).mean()
df['MA_60'] = df['收盘'].rolling(60).mean()
df['MA_120'] = df['收盘'].rolling(120).mean()
# 3. 波动率指标
df['波动率_20'] = df['日收益率'].rolling(20).std()
# 4. 技术指标
# RSI (14日)
delta = df['收盘'].diff()
gain = (delta.where(delta > 0, 0)).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs = gain / loss
df['RSI'] = 100 - (100 / (1 + rs))
# 布林带
df['BB_middle'] = df['收盘'].rolling(20).mean()
bb_std = df['收盘'].rolling(20).std()
df['BB_upper'] = df['BB_middle'] + 2 * bb_std
df['BB_lower'] = df['BB_middle'] - 2 * bb_std
print("\n技术指标:")
print(df[['收盘', 'MA_20', 'RSI', 'BB_upper', 'BB_lower']].tail(10))
# 5. 统计分析
print("\n收益率统计:")
print(df['日收益率'].describe())
# 6. 交易信号
df['信号'] = 0
# 金叉买入
df.loc[(df['MA_20'] > df['MA_60']) &
(df['MA_20'].shift(1) <= df['MA_60'].shift(1)), '信号'] = 1
# 死叉卖出
df.loc[(df['MA_20'] < df['MA_60']) &
(df['MA_20'].shift(1) >= df['MA_60'].shift(1)), '信号'] = -1
buy_signals = df[df['信号'] == 1]
sell_signals = df[df['信号'] == -1]
print(f"\n交易信号: 买入 {len(buy_signals)} 次, 卖出 {len(sell_signals)} 次")
# 7. 夏普比率(年化)
daily_sharpe = df['日收益率'].mean() / df['日收益率'].std()
annual_sharpe = daily_sharpe * np.sqrt(252)
print(f"年化夏普比率: {annual_sharpe:.3f}")
# 8. 最大回撤
cummax = df['收盘'].cummax()
drawdown = (df['收盘'] - cummax) / cummax * 100
max_drawdown = drawdown.min()
print(f"最大回撤: {max_drawdown:.2f}%")
return df
总结
Pandas核心优势
- 强大的数据结构:Series和DataFrame
- 完善的数据处理:清洗、转换、聚合
- 高效的计算能力:向量化操作
- 丰富的数据源支持:多种文件格式和数据库
- 时间序列分析:专门的时间处理功能
学习建议
- 循序渐进:先从基础数据结构开始
- 动手实践:每个概念都要写代码验证
- 结合实际:用真实数据练习
- 深入原理:理解底层数据结构和算法
- 持续学习:关注Pandas新版本功能
常用资源
- Pandas官方文档
- Python数据分析书籍
- Kaggle竞赛数据集
- 在线教程和视频
这份教程涵盖了Pandas的核心功能和使用场景,建议结合实际数据进行练习,逐步掌握数据分析的核心技能。

浙公网安备 33010602011771号