第二阶段_技术栈过渡-Day 2: Pandas基础

29 阅读19分钟

Day 2: Pandas基础

学习目标

  • 理解Pandas库的核心概念和数据结构
  • 掌握Series和DataFrame的创建和操作方法
  • 学习数据读写、清洗和转换技术
  • 了解Pandas在数据分析中的应用
  • 对比JAVA和Pandas在数据处理方面的差异

1. Pandas简介

1.1 什么是Pandas

Pandas是一个开源的Python数据分析库,提供了高性能、易用的数据结构和数据分析工具。它建立在NumPy之上,使数据处理、清洗和分析变得更加简单。

定义:Pandas是Python数据分析的核心库,提供了快速、灵活、富有表现力的数据结构,旨在使"关系"或"标记"数据的处理变得简单而直观。

核心特点

  • 强大的数据结构:Series(一维)和DataFrame(二维)
  • 处理各种格式数据的能力(CSV、Excel、SQL、JSON等)
  • 灵活的数据重塑和透视功能
  • 智能数据对齐和处理缺失数据
  • 时间序列功能和日期范围生成
  • 高性能合并和连接数据集

1.2 为什么需要Pandas

NumPy的局限性

  • 主要处理数值数据
  • 所有元素必须是相同类型
  • 缺乏处理表格数据的高级功能
  • 处理缺失值不便

Pandas的优势

  • 处理异构数据(不同列可以有不同类型)
  • 内置处理缺失值的功能
  • 强大的数据操作和分析功能
  • 类似SQL的数据操作
  • 与Excel类似的数据视图

1.3 Pandas与JAVA对比

特性JAVAPandas
数据结构集合框架(ArrayList, HashMap等)Series, DataFrame
数据处理流式API(Java 8+)或循环向量化操作
文件处理需要专门的库(Apache POI等)内置读写功能
数据分析需要第三方库内置分析功能
内存管理自动垃圾回收基于NumPy的优化存储

JAVA示例

// JAVA处理CSV数据
import java.io.*;
import java.util.*;

public class CSVProcessor {
    public static void main(String[] args) {
        List<Map<String, String>> data = new ArrayList<>();
        
        try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {
            String line;
            String[] headers = br.readLine().split(",");
            
            while ((line = br.readLine()) != null) {
                String[] values = line.split(",");
                Map<String, String> row = new HashMap<>();
                
                for (int i = 0; i < headers.length; i++) {
                    row.put(headers[i], values[i]);
                }
                
                data.add(row);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 过滤数据
        List<Map<String, String>> filtered = new ArrayList<>();
        for (Map<String, String> row : data) {
            if (Integer.parseInt(row.get("age")) > 30) {
                filtered.add(row);
            }
        }
        
        // 计算平均值
        double sum = 0;
        for (Map<String, String> row : filtered) {
            sum += Double.parseDouble(row.get("salary"));
        }
        double average = sum / filtered.size();
        
        System.out.println("Average salary: " + average);
    }
}

Pandas示例

# Pandas处理CSV数据
import pandas as pd

# 读取CSV
data = pd.read_csv("data.csv")

# 过滤数据
filtered = data[data['age'] > 30]

# 计算平均值
average = filtered['salary'].mean()

print(f"Average salary: {average}")

2. Pandas核心数据结构

2.1 Series

Series是Pandas中的一维标记数组,可以存储任何数据类型(整数、字符串、浮点数、Python对象等)。

创建Series

import pandas as pd
import numpy as np

# 从列表创建
s1 = pd.Series([1, 3, 5, 7, 9])
print(s1)
# 0    1
# 1    3
# 2    5
# 3    7
# 4    9
# dtype: int64

# 从NumPy数组创建
s2 = pd.Series(np.array([1, 3, 5, 7, 9]))
print(s2)

# 从字典创建
s3 = pd.Series({'a': 1, 'b': 3, 'c': 5})
print(s3)
# a    1
# b    3
# c    5
# dtype: int64

# 指定索引
s4 = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
print(s4)
# a    1
# b    3
# c    5
# d    7
# e    9
# dtype: int64

Series属性和方法

s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])

# 索引和值
print(s.index)  # Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
print(s.values)  # [1 3 5 7 9]

# 数据类型
print(s.dtype)  # int64

# 形状
print(s.shape)  # (5,)

# 大小
print(s.size)  # 5

# 是否唯一
print(s.is_unique)  # True

# 描述性统计
print(s.mean())  # 5.0
print(s.std())   # 3.1622776601683795
print(s.min())   # 1
print(s.max())   # 9
print(s.describe())
# count    5.000000
# mean     5.000000
# std      3.162278
# min      1.000000
# 25%      3.000000
# 50%      5.000000
# 75%      7.000000
# max      9.000000
# dtype: float64

Series索引和切片

s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])

# 按标签索引
print(s['a'])  # 1
print(s[['a', 'c', 'e']])
# a    1
# c    5
# e    9
# dtype: int64

# 按位置索引
print(s[0])  # 1
print(s[1:4])
# b    3
# c    5
# d    7
# dtype: int64

# 条件索引
print(s[s > 3])
# c    5
# d    7
# e    9
# dtype: int64

Series运算

s1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])

# 算术运算
print(s1 + s2)
# a    11
# b    22
# c    33
# d    44
# dtype: int64

# 标量运算
print(s1 * 2)
# a    2
# b    4
# c    6
# d    8
# dtype: int64

# 数学函数
print(np.sqrt(s1))
# a    1.000000
# b    1.414214
# c    1.732051
# d    2.000000
# dtype: float64

# 不同索引的运算(自动对齐)
s3 = pd.Series([10, 20, 30, 40], index=['a', 'b', 'x', 'y'])
print(s1 + s3)
# a    11.0
# b    22.0
# c     NaN
# d     NaN
# x     NaN
# y     NaN
# dtype: float64

2.2 DataFrame

DataFrame是Pandas中的二维标记数据结构,有行和列索引,可以看作是由多个Series组成的字典。

创建DataFrame

import pandas as pd
import numpy as np

# 从字典创建(每个键值对成为一列)
data = {
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42],
    'City': ['New York', 'Paris', 'Berlin', 'London']
}
df1 = pd.DataFrame(data)
print(df1)
#     Name  Age      City
# 0   John   28  New York
# 1   Anna   34     Paris
# 2  Peter   29    Berlin
# 3  Linda   42    London

# 指定列顺序
df2 = pd.DataFrame(data, columns=['Name', 'City', 'Age'])
print(df2)

# 从嵌套列表创建
data_list = [
    ['John', 28, 'New York'],
    ['Anna', 34, 'Paris'],
    ['Peter', 29, 'Berlin'],
    ['Linda', 42, 'London']
]
df3 = pd.DataFrame(data_list, columns=['Name', 'Age', 'City'])
print(df3)

# 从Series字典创建
data_series = {
    'Name': pd.Series(['John', 'Anna', 'Peter', 'Linda']),
    'Age': pd.Series([28, 34, 29, 42]),
    'City': pd.Series(['New York', 'Paris', 'Berlin', 'London'])
}
df4 = pd.DataFrame(data_series)
print(df4)

# 从NumPy数组创建
arr = np.array([['John', 28, 'New York'],
                ['Anna', 34, 'Paris'],
                ['Peter', 29, 'Berlin'],
                ['Linda', 42, 'London']])
df5 = pd.DataFrame(arr, columns=['Name', 'Age', 'City'])
print(df5)

DataFrame属性和方法

df = pd.DataFrame({
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42],
    'City': ['New York', 'Paris', 'Berlin', 'London']
})

# 基本信息
print(df.shape)  # (4, 3)
print(df.size)   # 12
print(df.dtypes)
# Name    object
# Age      int64
# City    object
# dtype: object

# 索引和列
print(df.index)  # RangeIndex(start=0, stop=4, step=1)
print(df.columns)  # Index(['Name', 'Age', 'City'], dtype='object')

# 值
print(df.values)
# [['John' 28 'New York']
#  ['Anna' 34 'Paris']
#  ['Peter' 29 'Berlin']
#  ['Linda' 42 'London']]

# 描述性统计
print(df.describe())
#              Age
# count   4.000000
# mean   33.250000
# std     6.397887
# min    28.000000
# 25%    28.750000
# 50%    31.500000
# 75%    36.000000
# max    42.000000

# 前几行和后几行
print(df.head(2))
#    Name  Age      City
# 0  John   28  New York
# 1  Anna   34     Paris

print(df.tail(2))
#     Name  Age    City
# 2  Peter   29  Berlin
# 3  Linda   42  London

# 信息摘要
print(df.info())
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 4 entries, 0 to 3
# Data columns (total 3 columns):
#  #   Column  Non-Null Count  Dtype 
# ---  ------  --------------  ----- 
#  0   Name    4 non-null      object
#  1   Age     4 non-null      int64 
#  2   City    4 non-null      object
# dtypes: int64(1), object(2)
# memory usage: 224.0+ bytes

DataFrame索引和选择

df = pd.DataFrame({
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42],
    'City': ['New York', 'Paris', 'Berlin', 'London']
})

# 选择列
print(df['Name'])  # 返回Series
print(df[['Name', 'Age']])  # 返回DataFrame

# 使用.loc按标签索引
print(df.loc[0])  # 返回Series(第一行)
print(df.loc[0:2])  # 返回DataFrame(前三行)
print(df.loc[0:2, 'Name':'Age'])  # 返回DataFrame(前三行,Name和Age列)

# 使用.iloc按位置索引
print(df.iloc[0])  # 返回Series(第一行)
print(df.iloc[0:2])  # 返回DataFrame(前两行)
print(df.iloc[0:2, 0:2])  # 返回DataFrame(前两行,前两列)

# 条件选择
print(df[df['Age'] > 30])
#     Name  Age    City
# 1   Anna   34   Paris
# 3  Linda   42  London

# 复杂条件
print(df[(df['Age'] > 30) & (df['City'] != 'London')])
#    Name  Age   City
# 1  Anna   34  Paris

DataFrame添加和修改数据

df = pd.DataFrame({
    'Name': ['John', 'Anna', 'Peter'],
    'Age': [28, 34, 29]
})

# 添加新列
df['City'] = ['New York', 'Paris', 'Berlin']
print(df)

# 使用函数添加列
df['Age in 5 Years'] = df['Age'] + 5
print(df)

# 修改列
df['Age'] = [30, 35, 31]
print(df)

# 添加行
df.loc[3] = ['Linda', 42, 'London', 47]
print(df)

# 使用append添加行(返回新DataFrame)
new_row = pd.DataFrame({'Name': ['Mike'], 'Age': [38], 'City': ['Madrid'], 'Age in 5 Years': [43]})
df = df.append(new_row, ignore_index=True)
print(df)

DataFrame删除数据

df = pd.DataFrame({
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42],
    'City': ['New York', 'Paris', 'Berlin', 'London']
})

# 删除列
df_no_age = df.drop('Age', axis=1)  # axis=1表示列
print(df_no_age)

# 删除多列
df_name_only = df.drop(['Age', 'City'], axis=1)
print(df_name_only)

# 删除行
df_no_first = df.drop(0)  # 删除第一行
print(df_no_first)

# 删除多行
df_middle = df.drop([0, 3])  # 删除第一行和第四行
print(df_middle)

# 原地修改
df.drop('Age', axis=1, inplace=True)
print(df)

3. 数据读写

3.1 读取数据

Pandas可以读取多种格式的数据,包括CSV、Excel、SQL、JSON等。

读取CSV文件

# 基本读取
df = pd.read_csv('data.csv')
print(df.head())

# 指定分隔符
df = pd.read_csv('data.tsv', sep='\t')
print(df.head())

# 指定列名
df = pd.read_csv('data.csv', names=['col1', 'col2', 'col3'])
print(df.head())

# 设置索引列
df = pd.read_csv('data.csv', index_col='ID')
print(df.head())

# 处理缺失值
df = pd.read_csv('data.csv', na_values=['NA', 'Missing'])
print(df.head())

# 只读取部分行
df = pd.read_csv('data.csv', nrows=10)
print(df)

# 跳过行
df = pd.read_csv('data.csv', skiprows=[0, 2, 3])
print(df)

读取Excel文件

# 基本读取
df = pd.read_excel('data.xlsx')
print(df.head())

# 指定工作表
df = pd.read_excel('data.xlsx', sheet_name='Sheet2')
print(df.head())

# 读取多个工作表
dfs = pd.read_excel('data.xlsx', sheet_name=['Sheet1', 'Sheet2'])
print(dfs['Sheet1'].head())
print(dfs['Sheet2'].head())

# 读取所有工作表
all_dfs = pd.read_excel('data.xlsx', sheet_name=None)
for name, df in all_dfs.items():
    print(f"Sheet: {name}")
    print(df.head())

读取SQL数据

import sqlite3

# 创建连接
conn = sqlite3.connect('database.db')

# 读取SQL查询结果
df = pd.read_sql('SELECT * FROM users', conn)
print(df.head())

# 读取整个表
df = pd.read_sql_table('users', conn)
print(df.head())

# 使用参数化查询
df = pd.read_sql('SELECT * FROM users WHERE age > ?', conn, params=(30,))
print(df.head())

# 关闭连接
conn.close()

读取JSON数据

# 基本读取
df = pd.read_json('data.json')
print(df.head())

# 读取嵌套JSON
df = pd.read_json('nested_data.json', orient='records')
print(df.head())

# 从字符串读取
json_str = '''
[{"name": "John", "age": 28, "city": "New York"},
 {"name": "Anna", "age": 34, "city": "Paris"},
 {"name": "Peter", "age": 29, "city": "Berlin"}]
'''
df = pd.read_json(json_str)
print(df)

3.2 写入数据

Pandas可以将DataFrame写入多种格式的文件。

写入CSV文件

df = pd.DataFrame({
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42],
    'City': ['New York', 'Paris', 'Berlin', 'London']
})

# 基本写入
df.to_csv('output.csv')

# 不包含索引
df.to_csv('output_no_index.csv', index=False)

# 指定分隔符
df.to_csv('output.tsv', sep='\t')

# 指定列
df.to_csv('output_selected.csv', columns=['Name', 'Age'])

# 指定缺失值表示
df.to_csv('output_na.csv', na_rep='Missing')

写入Excel文件

# 基本写入
df.to_excel('output.xlsx', sheet_name='Data')

# 不包含索引
df.to_excel('output_no_index.xlsx', index=False)

# 写入多个DataFrame到不同工作表
with pd.ExcelWriter('output_multiple.xlsx') as writer:
    df.to_excel(writer, sheet_name='People')
    df2 = pd.DataFrame({'Value': [10, 20, 30, 40]})
    df2.to_excel(writer, sheet_name='Values')

写入SQL数据库

import sqlite3

# 创建连接
conn = sqlite3.connect('output.db')

# 写入DataFrame到SQL表
df.to_sql('people', conn, if_exists='replace', index=False)

# 关闭连接
conn.close()

写入JSON

# 基本写入
df.to_json('output.json')

# 指定方向
df.to_json('output_records.json', orient='records')

# 格式化输出
df.to_json('output_pretty.json', orient='records', indent=4)

4. 数据清洗和预处理

4.1 处理缺失值

import pandas as pd
import numpy as np

# 创建包含缺失值的DataFrame
df = pd.DataFrame({
    'A': [1, 2, np.nan, 4],
    'B': [5, np.nan, np.nan, 8],
    'C': [9, 10, 11, 12]
})
print(df)
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  NaN  10
# 2  NaN  NaN  11
# 3  4.0  8.0  12

# 检查缺失值
print(df.isnull())
#        A      B      C
# 0  False  False  False
# 1  False   True  False
# 2   True   True  False
# 3  False  False  False

print(df.notnull())
#        A      B     C
# 0   True   True  True
# 1   True  False  True
# 2  False  False  True
# 3   True   True  True

# 缺失值统计
print(df.isnull().sum())
# A    1
# B    2
# C    0
# dtype: int64

# 删除缺失值
print(df.dropna())  # 删除包含任何缺失值的行
#      A    B   C
# 0  1.0  5.0   9
# 3  4.0  8.0  12

print(df.dropna(axis=1))  # 删除包含任何缺失值的列
#     C
# 0   9
# 1  10
# 2  11
# 3  12

print(df.dropna(how='all'))  # 只删除全部为缺失值的行
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  NaN  10
# 2  NaN  NaN  11
# 3  4.0  8.0  12

print(df.dropna(thresh=2))  # 至少有2个非缺失值的行
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  NaN  10
# 3  4.0  8.0  12

# 填充缺失值
print(df.fillna(0))  # 用0填充所有缺失值
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  0.0  10
# 2  0.0  0.0  11
# 3  4.0  8.0  12

print(df.fillna({'A': 0, 'B': 5}))  # 不同列使用不同的填充值
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  5.0  10
# 2  0.0  5.0  11
# 3  4.0  8.0  12

print(df.fillna(method='ffill'))  # 用前一个值填充
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  5.0  10
# 2  2.0  5.0  11
# 3  4.0  8.0  12

print(df.fillna(method='bfill'))  # 用后一个值填充
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  8.0  10
# 2  4.0  8.0  11
# 3  4.0  8.0  12

# 插值
print(df.interpolate())  # 线性插值
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  6.5  10
# 2  3.0  7.5  11
# 3  4.0  8.0  12

4.2 数据转换

# 创建示例DataFrame
df = pd.DataFrame({
    'A': [1, 2, 3, 4],
    'B': [5, 6, 7, 8],
    'C': ['a', 'b', 'c', 'd']
})

# 应用函数到列
print(df['A'].apply(lambda x: x * 2))
# 0    2
# 1    4
# 2    6
# 3    8
# Name: A, dtype: int64

# 应用函数到DataFrame
print(df.apply(lambda x: x.max()))
# A      4
# B      8
# C      d
# dtype: object

# 应用函数到行
print(df.apply(lambda x: x['A'] + x['B'], axis=1))
# 0     6
# 1     8
# 2    10
# 3    12
# dtype: int64

# 映射值
mapping = {'a': 'Apple', 'b': 'Banana', 'c': 'Cherry', 'd': 'Date'}
print(df['C'].map(mapping))
# 0     Apple
# 1    Banana
# 2    Cherry
# 3      Date
# Name: C, dtype: object

# 替换值
print(df['A'].replace({1: 100, 3: 300}))
# 0    100
# 1      2
# 2    300
# 3      4
# Name: A, dtype: int64

# 类型转换
df['A'] = df['A'].astype(float)
print(df.dtypes)
# A    float64
# B      int64
# C     object
# dtype: object

# 重命名列
print(df.rename(columns={'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}))
#    Alpha  Beta Charlie
# 0    1.0     5       a
# 1    2.0     6       b
# 2    3.0     7       c
# 3    4.0     8       d

4.3 数据规范化

# 创建示例DataFrame
df = pd.DataFrame({
    'A': [10, 20, 30, 40],
    'B': [100, 200, 300, 400],
    'C': [1000, 2000, 3000, 4000]
})

# 最小-最大缩放
def min_max_scale(x):
    return (x - x.min()) / (x.max() - x.min())

print(df.apply(min_max_scale))
#      A    B    C
# 0  0.0  0.0  0.0
# 1  0.3  0.3  0.3
# 2  0.7  0.7  0.7
# 3  1.0  1.0  1.0

# Z-score标准化
def z_score(x):
    return (x - x.mean()) / x.std()

print(df.apply(z_score))
#           A         B         C
# 0 -1.161895 -1.161895 -1.161895
# 1 -0.387298 -0.387298 -0.387298
# 2  0.387298  0.387298  0.387298
# 3  1.161895  1.161895  1.161895

# 使用sklearn进行规范化
from sklearn.preprocessing import MinMaxScaler, StandardScaler

# 最小-最大缩放
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df)
scaled_df = pd.DataFrame(scaled_data, columns=df.columns)
print(scaled_df)

# Z-score标准化
scaler = StandardScaler()
scaled_data = scaler.fit_transform(df)
scaled_df = pd.DataFrame(scaled_data, columns=df.columns)
print(scaled_df)

4.4 处理重复数据

# 创建包含重复行的DataFrame
df = pd.DataFrame({
    'A': [1, 2, 3, 1, 2],
    'B': [5, 6, 7, 5, 6],
    'C': ['a', 'b', 'c', 'a', 'b']
})

# 检查重复行
print(df.duplicated())
# 0    False
# 1    False
# 2    False
# 3     True
# 4     True
# dtype: bool

# 查找重复行
print(df[df.duplicated()])
#    A  B  C
# 3  1  5  a
# 4  2  6  b

# 查找所有重复行(包括第一次出现)
print(df[df.duplicated(keep=False)])
#    A  B  C
# 0  1  5  a
# 1  2  6  b
# 3  1  5  a
# 4  2  6  b

# 删除重复行
print(df.drop_duplicates())
#    A  B  C
# 0  1  5  a
# 1  2  6  b
# 2  3  7  c

# 基于特定列删除重复行
print(df.drop_duplicates(subset=['A']))
#    A  B  C
# 0  1  5  a
# 1  2  6  b
# 2  3  7  c

# 保留最后一次出现
print(df.drop_duplicates(keep='last'))
#    A  B  C
# 2  3  7  c
# 3  1  5  a
# 4  2  6  b

5. 数据分析和转换

5.1 分组和聚合

分组和聚合是数据分析中的核心操作,类似于SQL中的GROUP BY。

# 创建示例DataFrame
df = pd.DataFrame({
    'Category': ['A', 'A', 'A', 'B', 'B', 'C'],
    'Value1': [10, 20, 30, 40, 50, 60],
    'Value2': [100, 200, 300, 400, 500, 600]
})

# 按类别分组
grouped = df.groupby('Category')

# 计算每组的平均值
print(grouped.mean())
#           Value1  Value2
# Category                
# A            20.0   200.0
# B            45.0   450.0
# C            60.0   600.0

# 计算每组的总和
print(grouped.sum())
#           Value1  Value2
# Category                
# A            60     600
# B            90     900
# C            60     600

# 计算每组的计数
print(grouped.count())
#           Value1  Value2
# Category                
# A              3       3
# B              2       2
# C              1       1

# 应用多个聚合函数
print(grouped.agg(['sum', 'mean', 'count', 'min', 'max']))

# 对不同列应用不同的聚合函数
print(grouped.agg({'Value1': 'sum', 'Value2': 'mean'}))
#           Value1  Value2
# Category                
# A            60   200.0
# B            90   450.0
# C            60   600.0

# 使用自定义聚合函数
def range_diff(x):
    return x.max() - x.min()

print(grouped.agg(range_diff))
#           Value1  Value2
# Category                
# A            20     200
# B            10     100
# C             0       0

# 分组后过滤
print(grouped.filter(lambda x: x['Value1'].mean() > 30))
#   Category  Value1  Value2
# 3        B      40     400
# 4        B      50     500
# 5        C      60     600

5.2 透视表和交叉表

透视表和交叉表用于重塑数据和创建汇总报告。

# 创建示例DataFrame
df = pd.DataFrame({
    'Date': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03'],
    'Product': ['A', 'B', 'A', 'B', 'A'],
    'Region': ['East', 'West', 'East', 'West', 'East'],
    'Sales': [100, 200, 150, 250, 120]
})

# 创建透视表
pivot = df.pivot_table(
    values='Sales',
    index='Date',
    columns='Product',
    aggfunc='sum'
)
print(pivot)
# Product      A      B
# Date                  
# 2023-01-01  100  200.0
# 2023-01-02  150  250.0
# 2023-01-03  120    NaN

# 多级索引透视表
pivot_multi = df.pivot_table(
    values='Sales',
    index=['Date', 'Region'],
    columns='Product',
    aggfunc='sum'
)
print(pivot_multi)

# 使用多个聚合函数
pivot_multi_agg = df.pivot_table(
    values='Sales',
    index='Date',
    columns='Product',
    aggfunc=['sum', 'mean', 'count']
)
print(pivot_multi_agg)

# 创建交叉表
cross_tab = pd.crosstab(df['Region'], df['Product'])
print(cross_tab)
# Product  A  B
# Region       
# East     3  0
# West     0  2

# 带有值的交叉表
cross_tab_values = pd.crosstab(
    df['Region'],
    df['Product'],
    values=df['Sales'],
    aggfunc='sum'
)
print(cross_tab_values)
# Product      A      B
# Region                
# East     370.0    NaN
# West       NaN  450.0

5.3 合并和连接数据

合并和连接操作用于组合不同的DataFrame,类似于SQL中的JOIN操作。

# 创建示例DataFrame
df1 = pd.DataFrame({
    'ID': [1, 2, 3, 4],
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 34, 29, 42]
})

df2 = pd.DataFrame({
    'ID': [1, 2, 5, 6],
    'City': ['New York', 'Paris', 'Berlin', 'London'],
    'Salary': [50000, 60000, 55000, 65000]
})

# 内连接(只保留匹配的行)
merged_inner = pd.merge(df1, df2, on='ID', how='inner')
print(merged_inner)
#    ID  Name  Age      City  Salary
# 0   1  John   28  New York   50000
# 1   2  Anna   34     Paris   60000

# 左连接(保留左表的所有行)
merged_left = pd.merge(df1, df2, on='ID', how='left')
print(merged_left)
#    ID   Name  Age      City  Salary
# 0   1   John   28  New York   50000
# 1   2   Anna   34     Paris   60000
# 2   3  Peter   29       NaN     NaN
# 3   4  Linda   42       NaN     NaN

# 右连接(保留右表的所有行)
merged_right = pd.merge(df1, df2, on='ID', how='right')
print(merged_right)
#    ID   Name   Age      City  Salary
# 0   1   John  28.0  New York   50000
# 1   2   Anna  34.0     Paris   60000
# 2   5    NaN   NaN    Berlin   55000
# 3   6    NaN   NaN    London   65000

# 外连接(保留所有行)
merged_outer = pd.merge(df1, df2, on='ID', how='outer')
print(merged_outer)
#    ID   Name   Age      City  Salary
# 0   1   John  28.0  New York   50000
# 1   2   Anna  34.0     Paris   60000
# 2   3  Peter  29.0       NaN     NaN
# 3   4  Linda  42.0       NaN     NaN
# 4   5    NaN   NaN    Berlin   55000
# 5   6    NaN   NaN    London   65000

# 使用不同的键名
df2.columns = ['User_ID', 'City', 'Salary']  # 重命名ID列
merged = pd.merge(df1, df2, left_on='ID', right_on='User_ID')
print(merged)
#    ID  Name  Age  User_ID      City  Salary
# 0   1  John   28        1  New York   50000
# 1   2  Anna   34        2     Paris   60000

# 连接(按索引)
df3 = pd.DataFrame({
    'A': ['A0', 'A1', 'A2'],
    'B': ['B0', 'B1', 'B2']
}, index=['I0', 'I1', 'I2'])

df4 = pd.DataFrame({
    'C': ['C0', 'C1', 'C2'],
    'D': ['D0', 'D1', 'D2']
}, index=['I0', 'I2', 'I3'])

# 内连接
joined_inner = df3.join(df4, how='inner')
print(joined_inner)
#       A   B   C   D
# I0   A0  B0  C0  D0
# I2   A2  B2  C1  D1

# 左连接
joined_left = df3.join(df4, how='left')
print(joined_left)
#       A   B    C    D
# I0   A0  B0   C0   D0
# I1   A1  B1  NaN  NaN
# I2   A2  B2   C1   D1

# 连接多个DataFrame
df5 = pd.DataFrame({
    'E': ['E0', 'E1', 'E2']
}, index=['I0', 'I1', 'I4'])

joined_multiple = df3.join([df4, df5], how='outer')
print(joined_multiple)

5.4 时间序列数据

Pandas提供了强大的时间序列数据处理功能。

# 创建日期范围
dates = pd.date_range(start='2023-01-01', end='2023-01-10')
print(dates)
# DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04',
#                '2023-01-05', '2023-01-06', '2023-01-07', '2023-01-08',
#                '2023-01-09', '2023-01-10'],
#               dtype='datetime64[ns]', freq='D')

# 创建特定频率的日期范围
business_days = pd.date_range(start='2023-01-01', end='2023-01-15', freq='B')  # 工作日
print(business_days)

monthly = pd.date_range(start='2023-01-01', periods=12, freq='M')  # 月末
print(monthly)

hourly = pd.date_range(start='2023-01-01', periods=24, freq='H')  # 小时
print(hourly)

# 创建时间序列数据
np.random.seed(42)
ts = pd.Series(np.random.randn(len(dates)), index=dates)
print(ts)

# 时间序列重采样
print(ts.resample('3D').sum())  # 3天一组求和
print(ts.resample('W').mean())  # 按周求平均

# 时间序列移位
print(ts.shift(2))  # 向后移动2天
print(ts.shift(-1))  # 向前移动1天

# 时间序列滚动窗口计算
print(ts.rolling(window=3).mean())  # 3天滚动平均
print(ts.rolling(window=3).sum())   # 3天滚动求和

# 时间序列差分
print(ts.diff())  # 一阶差分

# 时间序列日期提取
dates_df = pd.DataFrame({'date': dates})
dates_df['year'] = dates_df['date'].dt.year
dates_df['month'] = dates_df['date'].dt.month
dates_df['day'] = dates_df['date'].dt.day
dates_df['weekday'] = dates_df['date'].dt.weekday
print(dates_df)

6. 从JAVA开发者视角理解Pandas

6.1 概念对应关系

JAVA概念Pandas概念
ArrayListSeries
HashMap/TableDataFrame
Stream API链式方法调用
JDBCread_sql/to_sql
POIread_excel/to_excel
Jackson/Gsonread_json/to_json
Comparatorsort_values
Filterboolean indexing

6.2 编程范式对比

JAVA命令式编程

// JAVA处理数据
List<Person> people = getPeople();
List<Person> filteredPeople = new ArrayList<>();

// 过滤
for (Person person : people) {
    if (person.getAge() > 30) {
        filteredPeople.add(person);
    }
}

// 排序
Collections.sort(filteredPeople, new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
});

// 转换
List<String> names = new ArrayList<>();
for (Person person : filteredPeople) {
    names.add(person.getName().toUpperCase());
}

JAVA流式API(Java 8+)

// JAVA流式API
List<String> names = people.stream()
    .filter(person -> person.getAge() > 30)
    .sorted(Comparator.comparing(Person::getName))
    .map(person -> person.getName().toUpperCase())
    .collect(Collectors.toList());

Pandas数据处理

# Pandas数据处理
names = (df[df['Age'] > 30]
         .sort_values('Name')
         ['Name']
         .str.upper()
         .tolist())

6.3 性能考量

JAVA数据处理

  • 类型安全但操作冗长
  • 内存管理更精细
  • 并行处理能力强
  • 适合大规模生产系统

Pandas数据处理

  • 语法简洁,分析便捷
  • 基于NumPy的向量化操作
  • 内存占用可能较大
  • 适合数据分析和原型开发

6.4 实现对比

JAVA实现数据分组和聚合

// JAVA实现分组和聚合
Map<String, List<Sale>> salesByCategory = sales.stream()
    .collect(Collectors.groupingBy(Sale::getCategory));

Map<String, Double> averageSalesByCategory = new HashMap<>();
for (Map.Entry<String, List<Sale>> entry : salesByCategory.entrySet()) {
    String category = entry.getKey();
    List<Sale> categorySales = entry.getValue();
    
    double total = 0;
    for (Sale sale : categorySales) {
        total += sale.getAmount();
    }
    
    averageSalesByCategory.put(category, total / categorySales.size());
}

Pandas实现数据分组和聚合

# Pandas实现分组和聚合
average_sales_by_category = df.groupby('Category')['Amount'].mean()

7. 实践练习

练习1:数据读取和基本操作

  1. 从CSV文件读取销售数据
  2. 显示数据的基本信息(形状、列名、数据类型等)
  3. 计算每列的描述性统计(均值、标准差、最小值、最大值等)
  4. 处理缺失值(检测、删除或填充)

练习2:数据过滤和转换

  1. 筛选出特定条件的数据(如销售额大于1000的记录)
  2. 创建新列(如利润 = 销售额 - 成本)
  3. 对数据进行排序(按销售日期和销售额)
  4. 使用apply函数对数据进行自定义转换

练习3:数据分析

  1. 按产品类别和地区对销售数据进行分组
  2. 计算每组的总销售额、平均销售额和销售次数
  3. 创建透视表显示不同地区和产品类别的销售情况
  4. 使用时间序列分析查看销售趋势(按月、按季度)

8. 总结与反思

  • Pandas是Python数据分析的核心库,提供了强大的数据结构和数据处理功能
  • Series和DataFrame是Pandas的两个核心数据结构,分别用于处理一维和二维数据
  • Pandas支持多种格式的数据读写,包括CSV、Excel、SQL、JSON等
  • 数据清洗和预处理功能(处理缺失值、重复数据、数据转换等)使数据分析更加便捷
  • 分组、聚合、透视表和合并等功能提供了强大的数据分析能力
  • 相比JAVA的数据处理,Pandas提供了更简洁的语法和更丰富的功能
  • JAVA开发者可以利用已有的集合框架和流式API概念快速理解Pandas

9. 预习与延伸阅读

预习内容

  • Matplotlib和Seaborn的基本概念和绘图功能
  • 常见图表类型(折线图、柱状图、散点图等)
  • 图表定制和美化方法
  • 多子图和交互式可视化

延伸阅读

  1. Wes McKinney,《Python for Data Analysis》(第5-10章)
  2. Jake VanderPlas,《Python Data Science Handbook》(第3章)
  3. 官方文档:pandas.pydata.org/docs/
  4. 10 Minutes to Pandas教程:pandas.pydata.org/docs/user_g…

10. 明日预告

明天我们将学习数据可视化库Matplotlib和Seaborn,这些工具可以将Pandas处理后的数据转化为直观的图表。我们将探讨各种图表类型、图表定制和美化方法,以及如何创建交互式可视化。这些技能对于数据分析和机器学习结果展示至关重要。