📚 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核心优势

  1. 强大的数据结构:Series和DataFrame
  2. 完善的数据处理:清洗、转换、聚合
  3. 高效的计算能力:向量化操作
  4. 丰富的数据源支持:多种文件格式和数据库
  5. 时间序列分析:专门的时间处理功能

学习建议

  1. 循序渐进:先从基础数据结构开始
  2. 动手实践:每个概念都要写代码验证
  3. 结合实际:用真实数据练习
  4. 深入原理:理解底层数据结构和算法
  5. 持续学习:关注Pandas新版本功能

常用资源

  • Pandas官方文档
  • Python数据分析书籍
  • Kaggle竞赛数据集
  • 在线教程和视频

这份教程涵盖了Pandas的核心功能和使用场景,建议结合实际数据进行练习,逐步掌握数据分析的核心技能。

posted @ 2026-07-17 20:19  yuangu  阅读(7)  评论(0)    收藏  举报