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对比
| 特性 | JAVA | Pandas |
|---|---|---|
| 数据结构 | 集合框架(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概念 |
|---|---|
| ArrayList | Series |
| HashMap/Table | DataFrame |
| Stream API | 链式方法调用 |
| JDBC | read_sql/to_sql |
| POI | read_excel/to_excel |
| Jackson/Gson | read_json/to_json |
| Comparator | sort_values |
| Filter | boolean 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:数据读取和基本操作
- 从CSV文件读取销售数据
- 显示数据的基本信息(形状、列名、数据类型等)
- 计算每列的描述性统计(均值、标准差、最小值、最大值等)
- 处理缺失值(检测、删除或填充)
练习2:数据过滤和转换
- 筛选出特定条件的数据(如销售额大于1000的记录)
- 创建新列(如利润 = 销售额 - 成本)
- 对数据进行排序(按销售日期和销售额)
- 使用apply函数对数据进行自定义转换
练习3:数据分析
- 按产品类别和地区对销售数据进行分组
- 计算每组的总销售额、平均销售额和销售次数
- 创建透视表显示不同地区和产品类别的销售情况
- 使用时间序列分析查看销售趋势(按月、按季度)
8. 总结与反思
- Pandas是Python数据分析的核心库,提供了强大的数据结构和数据处理功能
- Series和DataFrame是Pandas的两个核心数据结构,分别用于处理一维和二维数据
- Pandas支持多种格式的数据读写,包括CSV、Excel、SQL、JSON等
- 数据清洗和预处理功能(处理缺失值、重复数据、数据转换等)使数据分析更加便捷
- 分组、聚合、透视表和合并等功能提供了强大的数据分析能力
- 相比JAVA的数据处理,Pandas提供了更简洁的语法和更丰富的功能
- JAVA开发者可以利用已有的集合框架和流式API概念快速理解Pandas
9. 预习与延伸阅读
预习内容
- Matplotlib和Seaborn的基本概念和绘图功能
- 常见图表类型(折线图、柱状图、散点图等)
- 图表定制和美化方法
- 多子图和交互式可视化
延伸阅读
- Wes McKinney,《Python for Data Analysis》(第5-10章)
- Jake VanderPlas,《Python Data Science Handbook》(第3章)
- 官方文档:pandas.pydata.org/docs/
- 10 Minutes to Pandas教程:pandas.pydata.org/docs/user_g…
10. 明日预告
明天我们将学习数据可视化库Matplotlib和Seaborn,这些工具可以将Pandas处理后的数据转化为直观的图表。我们将探讨各种图表类型、图表定制和美化方法,以及如何创建交互式可视化。这些技能对于数据分析和机器学习结果展示至关重要。