4.2 Python 中的 CSV 文件读写与 pandas 库
引言:数据交换的通用语言
在数据科学和日常数据处理中,CSV(Comma-Separated Values,逗号分隔值)文件无疑是最常见、最通用的数据交换格式之一。它的优势在于:
- 简单性:纯文本格式,人类可读,可以用任何文本编辑器打开和编辑
- 通用性:几乎所有的数据处理工具、编程语言和电子表格软件(如Excel)都支持CSV
- 轻量级:相比二进制格式,CSV文件更小,传输更快
Python提供了多种处理CSV文件的方式,从标准库的csv模块到强大的第三方库pandas。本章将全面探讨这两种方法,重点展示如何利用pandas这一数据分析利器高效地处理CSV数据,并通过实战项目巩固所学知识。
第一部分:Python标准库csv模块详解
虽然本章重点在pandas,但了解标准库的csv模块是理解CSV处理基础的重要一步。
1.1 读取CSV文件
csv.reader对象提供了逐行读取CSV文件的基本功能:
import csv
# 基本读取
with open('data.csv', 'r', newline='', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
print(row) # 每一行是一个字符串列表
# 处理包含标题的CSV
with open('data_with_header.csv', 'r', newline='', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
headers = next(reader) # 获取第一行作为标题
print("Headers:", headers)
for row in reader:
print(row) # 数据行
1.2 写入CSV文件
csv.writer对象用于将数据写入CSV文件:
import csv
data = [
['Name', 'Age', 'City'],
['Alice', 25, 'New York'],
['Bob', 30, 'London'],
['Charlie', 35, 'Tokyo']
]
# 基本写入
with open('output.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(data)
# 自定义分隔符和引号规则
with open('output_semicolon.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
writer.writerows(data)
1.3 使用DictReader和DictWriter处理字典形式数据
csv模块还提供了更高级的DictReader和DictWriter类,它们以字典形式处理数据,使代码更易读:
import csv
# 读取CSV为字典列表
with open('data_with_header.csv', 'r', newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
print(f"{row['Name']} is {row['Age']} years old and lives in {row['City']}")
# 写入字典数据到CSV
data_dicts = [
{'Name': 'Alice', 'Age': 25, 'City': 'New York'},
{'Name': 'Bob', 'Age': 30, 'City': 'London'},
{'Name': 'Charlie', 'Age': 35, 'City': 'Tokyo'}
]
with open('output_dict.csv', 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['Name', 'Age', 'City']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader() # 写入标题行
writer.writerows(data_dicts)
第二部分:pandas库入门
pandas是Python数据分析的核心库,提供了高效、易用的数据结构和数据分析工具,特别适合处理表格数据。
2.1 pandas安装与核心数据结构
首先确保安装了pandas:
pip install pandas
pandas有两个核心数据结构:
- Series:一维数组,类似于带标签的列表
- DataFrame:二维表格,类似于Excel工作表或SQL表,是pandas中最常用的数据结构
2.2 使用pandas读取CSV文件
pandas提供了简单强大的CSV读取功能:
import pandas as pd
# 基本读取
df = pd.read_csv('data.csv')
print(df)
# 查看数据基本信息
print(df.info()) # 数据类型、非空值数量等
print(df.describe()) # 数值列的统计信息
# 读取时指定参数
df = pd.read_csv('data.csv',
encoding='utf-8', # 编码
sep=',', # 分隔符,默认为逗号
header=0, # 标题行位置,None表示无标题
index_col=0, # 将哪一列作为行索引
usecols=[0, 1, 3], # 只读取指定列
nrows=100) # 只读取前100行
2.3 使用pandas写入CSV文件
将DataFrame保存为CSV文件同样简单:
# 基本保存
df.to_csv('output.csv', index=False) # index=False表示不保存行索引
# 更多选项
df.to_csv('output.csv',
index=False,
encoding='utf-8',
sep=';', # 使用分号作为分隔符
float_format='%.2f') # 浮点数格式
第三部分:pandas数据处理实战
pandas的真正强大之处在于其丰富的数据处理功能。
3.1 数据清洗
真实世界的数据往往不完美,pandas提供了多种数据清洗方法:
# 处理缺失值
df.fillna(0, inplace=True) # 用0填充所有缺失值
df['Age'].fillna(df['Age'].mean(), inplace=True) # 用平均值填充特定列的缺失值
df.dropna(inplace=True) # 删除包含缺失值的行
# 处理重复值
df.drop_duplicates(inplace=True) # 删除完全重复的行
df.drop_duplicates(subset=['Email'], keep='first', inplace=True) # 基于特定列删除重复值
# 数据类型转换
df['Age'] = df['Age'].astype(int) # 转换为整数
df['Date'] = pd.to_datetime(df['Date']) # 转换为日期时间类型
# 重命名列
df.rename(columns={'old_name': 'new_name'}, inplace=True)
# 重置索引
df.reset_index(drop=True, inplace=True) # 删除旧索引,创建新整数索引
3.2 数据筛选与排序
pandas提供了灵活的数据筛选和排序功能:
# 条件筛选
adults = df[df['Age'] >= 18] # 筛选年龄大于等于18的行
ny_residents = df[df['City'] == 'New York'] # 筛选纽约居民
high_income_adults = df[(df['Age'] >= 18) & (df['Income'] > 50000)] # 多条件筛选
# 使用query方法筛选
results = df.query('Age >= 18 and Income > 50000')
# 排序
df_sorted = df.sort_values('Age') # 按年龄升序排序
df_sorted = df.sort_values(['City', 'Age'], ascending=[True, False]) # 先按城市升序,再按年龄降序
3.3 数据分组与聚合
分组聚合是数据分析的核心操作:
# 基本分组
grouped = df.groupby('City')
# 聚合计算
city_stats = grouped.agg({
'Age': ['mean', 'min', 'max', 'count'],
'Income': 'sum'
})
print(city_stats)
# 更简洁的语法
city_stats = df.groupby('City')['Age'].mean() # 每个城市的平均年龄
city_income = df.groupby('City')['Income'].sum() # 每个城市的总收入
# 多级分组
multi_group = df.groupby(['City', 'Gender'])
multi_stats = multi_group['Income'].mean()
3.4 数据转换与创建新列
pandas可以轻松地对数据进行转换和创建新列:
# 创建新列
df['Age_Group'] = pd.cut(df['Age'],
bins=[0, 18, 35, 55, 100],
labels=['Child', 'Young Adult', 'Adult', 'Senior'])
df['Income_per_Age'] = df['Income'] / df['Age'] # 基于现有列计算新列
# 应用函数
df['Name_Length'] = df['Name'].apply(len) # 应用Python内置函数
df['Category'] = df['Product'].apply(lambda x: 'Electronics' if 'phone' in x.lower() else 'Other') # 应用自定义函数
# 使用assign方法链式创建新列
df = df.assign(
Income_Squared = df['Income'] ** 2,
Log_Income = np.log(df['Income']) # 需要导入numpy as np
)
第四部分:性能优化与高级技巧
4.1 处理大型CSV文件
对于非常大的CSV文件,可以采取以下策略:
# 分块读取
chunk_size = 10000 # 每次读取10000行
chunks = pd.read_csv('large_file.csv', chunksize=chunk_size)
for chunk in chunks:
process_chunk(chunk) # 处理每个数据块
# 只读取需要的列
df = pd.read_csv('large_file.csv', usecols=['col1', 'col2', 'col5'])
# 指定数据类型以减少内存使用
dtypes = {
'Age': 'int8',
'Income': 'float32',
'Name': 'category'
}
df = pd.read_csv('large_file.csv', dtype=dtypes)
4.2 处理日期和时间数据
pandas提供了强大的日期时间处理功能:
# 解析日期列
df['Date'] = pd.to_datetime(df['Date'], format='%Y-%m-%d')
# 提取日期组成部分
df['Year'] = df['Date'].dt.year
df['Month'] = df['Date'].dt.month
df['Day'] = df['Date'].dt.day
df['Weekday'] = df['Date'].dt.day_name()
# 日期运算
df['Days_Since'] = (pd.Timestamp.now() - df['Date']).dt.days
# 重采样时间序列数据
df.set_index('Date', inplace=True)
monthly_data = df.resample('M').mean() # 按月重采样并计算平均值
第五部分:综合实战项目——销售数据分析系统
现在,让我们运用所学知识构建一个完整的销售数据分析系统,该系统将:
- 从多个CSV文件读取销售数据
- 清洗和预处理数据
- 进行多维度销售分析
- 生成分析报告并保存结果
项目结构:
sales_analysis/
├── data/
│ ├── sales_2023_q1.csv
│ ├── sales_2023_q2.csv
│ └── products.csv
├── sales_analyzer.py
└── reports/
代码实现:
# sales_analyzer.py
import pandas as pd
import numpy as np
import os
from datetime import datetime
class SalesAnalyzer:
def __init__(self, data_dir='data'):
self.data_dir = data_dir
self.sales_data = None
self.products_data = None
def load_data(self):
"""加载所有销售数据和产品数据"""
sales_files = [f for f in os.listdir(self.data_dir) if f.startswith('sales_')]
sales_dfs = []
for file in sales_files:
file_path = os.path.join(self.data_dir, file)
df = pd.read_csv(file_path)
sales_dfs.append(df)
# 合并所有销售数据
self.sales_data = pd.concat(sales_dfs, ignore_index=True)
# 加载产品数据
products_path = os.path.join(self.data_dir, 'products.csv')
self.products_data = pd.read_csv(products_path)
print("数据加载完成!")
print(f"销售记录数: {len(self.sales_data)}")
print(f"产品数: {len(self.products_data)}")
def clean_data(self):
"""数据清洗和预处理"""
# 处理缺失值
self.sales_data.fillna({'Discount': 0}, inplace=True)
# 删除金额为负或为零的记录
self.sales_data = self.sales_data[self.sales_data['Amount'] > 0]
# 转换日期列
self.sales_data['SaleDate'] = pd.to_datetime(self.sales_data['SaleDate'])
# 提取日期组成部分
self.sales_data['Year'] = self.sales_data['SaleDate'].dt.year
self.sales_data['Month'] = self.sales_data['SaleDate'].dt.month
self.sales_data['Quarter'] = self.sales_data['SaleDate'].dt.quarter
self.sales_data['DayOfWeek'] = self.sales_data['SaleDate'].dt.day_name()
# 计算实际销售额(考虑折扣)
self.sales_data['FinalAmount'] = self.sales_data['Amount'] * (1 - self.sales_data['Discount'])
# 合并产品信息
self.sales_data = pd.merge(self.sales_data, self.products_data, on='ProductID', how='left')
print("数据清洗完成!")
def analyze_sales(self):
"""进行销售分析"""
analysis_results = {}
# 总体销售统计
total_sales = self.sales_data['FinalAmount'].sum()
total_orders = len(self.sales_data)
avg_order_value = total_sales / total_orders
analysis_results['overall'] = {
'total_sales': total_sales,
'total_orders': total_orders,
'avg_order_value': avg_order_value
}
# 按产品类别分析
category_sales = self.sales_data.groupby('Category').agg({
'FinalAmount': ['sum', 'count'],
'ProductID': 'nunique'
}).round(2)
category_sales.columns = ['Sales_Amount', 'Order_Count', 'Unique_Products']
analysis_results['by_category'] = category_sales
# 按月分析销售趋势
monthly_sales = self.sales_data.groupby(['Year', 'Month']).agg({
'FinalAmount': 'sum',
'OrderID': 'count'
}).rename(columns={'FinalAmount': 'Monthly_Sales', 'OrderID': 'Order_Count'})
analysis_results['monthly_trend'] = monthly_sales
# 按星期分析销售模式
weekday_sales = self.sales_data.groupby('DayOfWeek').agg({
'FinalAmount': 'sum',
'OrderID': 'count'
}).rename(columns={'FinalAmount': 'Daily_Sales', 'OrderID': 'Order_Count'})
analysis_results['weekday_pattern'] = weekday_sales
# Top 10产品
top_products = self.sales_data.groupby('ProductName').agg({
'FinalAmount': 'sum',
'OrderID': 'count'
}).rename(columns={'FinalAmount': 'Total_Sales', 'OrderID': 'Times_Ordered'})
top_products = top_products.sort_values('Total_Sales', ascending=False).head(10)
analysis_results['top_products'] = top_products
return analysis_results
def generate_report(self, analysis_results, report_dir='reports'):
"""生成分析报告"""
# 确保报告目录存在
os.makedirs(report_dir, exist_ok=True)
# 生成时间戳
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
# 保存详细分析结果
for key, result in analysis_results.items():
if hasattr(result, 'to_csv'):
filename = f"{key}_analysis_{timestamp}.csv"
filepath = os.path.join(report_dir, filename)
result.to_csv(filepath)
# 生成摘要报告
summary_path = os.path.join(report_dir, f"sales_summary_{timestamp}.txt")
with open(summary_path, 'w') as f:
f.write("销售分析报告摘要\n")
f.write("=" * 50 + "\n\n")
overall = analysis_results['overall']
f.write(f"总销售额: ${overall['total_sales']:,.2f}\n")
f.write(f"总订单数: {overall['total_orders']:,}\n")
f.write(f"平均订单价值: ${overall['avg_order_value']:,.2f}\n\n")
f.write("按产品类别销售情况:\n")
for category, row in analysis_results['by_category'].iterrows():
f.write(f" {category}: ${row['Sales_Amount']:,.2f} ({row['Order_Count']} 订单)\n")
f.write("\nTop 5畅销产品:\n")
for product, row in analysis_results['top_products'].head().iterrows():
f.write(f" {product}: ${row['Total_Sales']:,.2f}\n")
print(f"报告已生成到 {report_dir} 目录!")
def run_analysis(self):
"""运行完整分析流程"""
print("开始销售数据分析...")
self.load_data()
self.clean_data()
results = self.analyze_sales()
self.generate_report(results)
print("分析完成!")
# 主程序
if __name__ == '__main__':
analyzer = SalesAnalyzer()
analyzer.run_analysis()
项目扩展思路:
- 添加可视化功能,使用matplotlib或seaborn生成销售趋势图表
- 添加预测功能,使用时间序列分析预测未来销售
- 创建Web界面,使用Flask或Streamlit展示分析结果
- 添加数据库支持,直接从数据库读取数据而非CSV文件
- 实现自动化报告发送,通过电子邮件发送每日/每周销售报告
总结
通过本章的学习,你已经掌握了使用pandas处理CSV文件的全面技能。以下是关键要点:
- 选择合适的工具:对于简单CSV操作,标准库的csv模块足够;对于复杂数据处理,pandas是不二之选。
- 始终指定编码:在处理CSV文件时,明确指定
encoding='utf-8'可以避免大多数乱码问题。 - 分块处理大文件:使用
chunksize参数处理大型CSV文件,避免内存不足。 - 利用向量化操作:pandas的向量化操作比循环快几个数量级,尽量避免使用
apply和迭代。 - 探索pandas的强大功能:pandas提供了丰富的数据处理功能,如分组聚合、数据透视表、时间序列处理等,值得深入探索。
CSV文件和pandas库是Python数据分析生态系统的基础组成部分。掌握它们不仅能够处理日常的数据任务,也为学习更高级的数据科学和机器学习技术奠定了坚实基础。
更多推荐



所有评论(0)