Python数据分析入门 - 从Pandas到可视化
Python Data Analysis Beginner Guide - From Pandas to Visualization
引言:数据分析为何重要以及Python为何是最佳选择
我们生活在一个每天产生海量数据的时代。从企业的销售数据、社交媒体的用户行为日志到政府的公共统计资料,所有这些数据中都隐藏着有意义的模式和洞察。数据分析就是系统地整理和分析这些原始数据,提取有助于决策的信息的过程。从制定营销策略到产品改进、风险预测,数据分析已成为几乎所有行业的核心竞争力。
那么,在众多编程语言中,为什么Python成为了数据分析的代表性语言呢?首先,Python语法直观易读,即使是编程初学者也能快速上手。其次,它拥有Pandas、NumPy、Matplotlib、Seaborn等专门用于数据分析的强大库生态系统。第三,通过Jupyter Notebook这一交互式开发环境,可以即时查看代码执行结果,边分析边验证。第四,Python拥有全球最大的开发者社区,便于查找解决问题所需的资料。
本文将从环境搭建到实战项目,逐步引导您了解Python数据分析的基础知识。即使是完全没有编程经验的初学者,按照本指南操作后也能够进行基本的数据分析。
1. 环境搭建:分析的第一步
1.1 安装Python
要开始数据分析,首先需要安装Python。截至2026年,推荐使用Python 3.12及以上版本。您可以从官方网站(python.org)下载适合您操作系统的安装程序。安装时务必勾选"Add Python to PATH"选项,这样才能直接在终端中运行Python。
安装完成后,在终端(命令提示符)中验证版本。
# 检查Python版本
python --version
# 输出示例: Python 3.12.x
# 检查pip版本
pip --version
1.2 使用Anaconda一站式安装
如果逐个安装数据分析所需的库比较麻烦,推荐使用Anaconda发行版。Anaconda会同时安装Python以及Pandas、NumPy、Matplotlib、Jupyter Notebook等数据科学所需的主要软件包。
# 安装Anaconda后创建虚拟环境
conda create -n data_analysis python=3.12
conda activate data_analysis
# 所需软件包已包含在内,如需额外安装
conda install pandas numpy matplotlib seaborn jupyter
1.3 开始使用Jupyter Notebook
Jupyter Notebook是数据分析师的必备工具。它允许按单元格执行代码并立即查看结果,非常适合探索性数据分析(EDA)。利用Markdown单元格还可以同时记录分析过程的说明。
# 使用pip安装
pip install jupyter notebook
# 启动Jupyter Notebook
jupyter notebook
# 或启动JupyterLab(下一代版本)
pip install jupyterlab
jupyter lab
浏览器会自动打开并显示Jupyter仪表板。点击右上角的"New > Python 3"即可创建新的笔记本。
2. Pandas基础:数据分析的核心库
2.1 理解DataFrame和Series
Pandas是Python数据分析的核心库。理解Pandas的两种主要数据结构——Series和DataFrame是第一步。Series是一维数组(单列),DataFrame是由行和列组成的二维表格(类似于Excel电子表格)。
import pandas as pd
import numpy as np
# 创建Series
s = pd.Series([10, 20, 30, 40, 50],
index=['a', 'b', 'c', 'd', 'e'])
print(s)
# 输出:
# a 10
# b 20
# c 30
# d 40
# e 50
# dtype: int64
# 创建DataFrame
data = {
'姓名': ['张三', '李四', '王五', '赵六', '陈七'],
'年龄': [25, 30, 28, 35, 22],
'城市': ['北京', '上海', '广州', '北京', '深圳'],
'分数': [85, 92, 78, 95, 88]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# 姓名 年龄 城市 分数
# 0 张三 25 北京 85
# 1 李四 30 上海 92
# 2 王五 28 广州 78
# 3 赵六 35 北京 95
# 4 陈七 22 深圳 88
2.2 数据的读取与写入
在实际的数据分析中,需要读取CSV、Excel、JSON等各种格式的文件。Pandas为此提供了便捷的函数。
# 读取CSV文件
df_csv = pd.read_csv('data.csv', encoding='utf-8')
# 读取Excel文件
df_excel = pd.read_excel('data.xlsx', sheet_name='Sheet1')
# 读取JSON文件
df_json = pd.read_json('data.json')
# 从网页URL直接读取
url = 'https://example.com/data.csv'
df_web = pd.read_csv(url)
# 查看DataFrame基本信息
print(df_csv.shape) # (行数, 列数)
print(df_csv.info()) # 列名、类型、缺失值信息
print(df_csv.describe()) # 数值列的统计摘要
print(df_csv.head(10)) # 显示前10行
# 保存到文件
df_csv.to_csv('output.csv', index=False, encoding='utf-8-sig')
df_csv.to_excel('output.xlsx', index=False)
2.3 索引与筛选
从数据中选择和提取特定部分是数据分析最基本的操作。Pandas提供了多种索引方法。
# 选择列
print(df['姓名']) # 单列(返回Series)
print(df[['姓名', '分数']]) # 多列(返回DataFrame)
# 选择行 - loc(基于标签)
print(df.loc[0]) # 索引为0的行
print(df.loc[0:2]) # 索引0到2的行(包含末尾)
# 选择行 - iloc(基于整数位置)
print(df.iloc[0]) # 第一行
print(df.iloc[0:2]) # 第一到第二行(不包含末尾)
# 条件筛选
print(df[df['年龄'] >= 28]) # 年龄28及以上
print(df[df['城市'] == '北京']) # 北京居民
print(df[(df['年龄'] >= 25) & (df['分数'] >= 90)]) # 复合条件
# 根据是否包含特定值进行筛选
cities = ['北京', '上海']
print(df[df['城市'].isin(cities)])
# 根据字符串包含进行筛选
print(df[df['姓名'].str.contains('张')])
3. 数据预处理:决定分析质量的关键步骤
现实中的数据并不完美。存在缺失值(空值)、重复数据、错误格式等各种问题。数据预处理就是解决这些问题,将数据整理成适合分析的形式的过程。据了解,在实际的数据分析工作中,预处理占据了总工作时间的60~80%,可见其重要性。
3.1 缺失值(Missing Value)处理
# 创建包含缺失值的示例数据
data = {
'姓名': ['张三', '李四', None, '赵六', '陈七'],
'年龄': [25, np.nan, 28, 35, 22],
'城市': ['北京', '上海', '广州', None, '深圳'],
'分数': [85, 92, np.nan, 95, 88]
}
df = pd.DataFrame(data)
# 检查缺失值
print(df.isnull().sum()) # 每列的缺失值数量
print(df.isnull().sum().sum()) # 总缺失值数量
# 删除缺失值
df_dropped = df.dropna() # 删除所有包含缺失值的行
df_dropped_col = df.dropna(axis=1) # 删除所有包含缺失值的列
df_thresh = df.dropna(thresh=3) # 仅保留非缺失值不少于3个的行
# 填充缺失值
df['年龄'] = df['年龄'].fillna(df['年龄'].mean()) # 用均值填充
df['城市'] = df['城市'].fillna('未知') # 用特定值填充
df['分数'] = df['分数'].fillna(method='ffill') # 用前一个值填充
df['姓名'] = df['姓名'].fillna('未知') # 用字符串填充
3.2 删除重复数据
# 检查重复行
print(df.duplicated().sum()) # 总重复行数
print(df.duplicated(subset=['姓名'])) # 按特定列检查重复
# 删除重复
df_unique = df.drop_duplicates() # 基于所有列
df_unique_name = df.drop_duplicates(subset=['姓名']) # 基于'姓名'列
df_unique_last = df.drop_duplicates(
subset=['姓名'], keep='last' # 保留最后一个值
)
3.3 数据类型转换
# 查看当前数据类型
print(df.dtypes)
# 类型转换
df['年龄'] = df['年龄'].astype(int) # 转换为整数
df['分数'] = df['分数'].astype(float) # 转换为浮点数
df['姓名'] = df['姓名'].astype(str) # 转换为字符串
# 转换为日期类型
df['日期'] = pd.to_datetime(df['日期字符串'], format='%Y-%m-%d')
# 转换为分类类型(有效节省内存)
df['城市'] = df['城市'].astype('category')
3.4 分组与聚合
按特定标准对数据进行分组和聚合是从数据中发现有意义模式的核心技术。
# 按城市计算平均分数
print(df.groupby('城市')['分数'].mean())
# 按城市一次计算多个统计量
print(df.groupby('城市')['分数'].agg(['mean', 'max', 'min', 'count']))
# 按多列分组
print(df.groupby(['城市', '性别'])['分数'].mean())
# 数据透视表(类似于Excel的数据透视表)
pivot = df.pivot_table(
values='分数',
index='城市',
columns='性别',
aggfunc='mean',
fill_value=0
)
print(pivot)
4. 数据可视化:Matplotlib与Seaborn
仅凭数字难以直观地把握数据的模式和趋势。可视化将数据以图表形式呈现,使复杂的信息一目了然。在Python中,Matplotlib(基础可视化)和Seaborn(统计可视化)是使用最广泛的库。
4.1 Matplotlib基础
import matplotlib.pyplot as plt
# 中文字体设置(Windows环境)
plt.rcParams['font.family'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False # 防止负号显示异常
# 1. 折线图(Line Plot)- 适合追踪随时间变化的趋势
months = ['1月', '2月', '3月', '4月', '5月', '6月']
sales = [150, 180, 200, 170, 220, 250]
plt.figure(figsize=(10, 6))
plt.plot(months, sales, marker='o', linewidth=2, color='#3498db')
plt.title('月度销售趋势', fontsize=16, fontweight='bold')
plt.xlabel('月份', fontsize=12)
plt.ylabel('销售额(万元)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('line_chart.png', dpi=150)
plt.show()
# 2. 柱状图(Bar Chart)- 适合类别间比较
categories = ['电子产品', '服装', '食品', '家具', '图书']
values = [450, 320, 280, 190, 150]
plt.figure(figsize=(10, 6))
bars = plt.bar(categories, values, color=['#3498db', '#2ecc71', '#e74c3c',
'#f39c12', '#9b59b6'])
plt.title('各类别销售量', fontsize=16, fontweight='bold')
plt.xlabel('类别', fontsize=12)
plt.ylabel('销售量', fontsize=12)
# 在每个柱状体顶部显示数值
for bar, val in zip(bars, values):
plt.text(bar.get_x() + bar.get_width()/2., bar.get_height() + 5,
str(val), ha='center', va='bottom', fontsize=11)
plt.tight_layout()
plt.show()
# 3. 饼图(Pie Chart)- 适合显示比例
labels = ['北京', '上海', '广州', '深圳', '其他']
sizes = [35, 25, 15, 10, 15]
colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6']
explode = (0.05, 0, 0, 0, 0) # 稍微分离北京的扇区
plt.figure(figsize=(8, 8))
plt.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=90)
plt.title('各地区用户分布', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
4.2 使用Seaborn进行高级可视化
Seaborn基于Matplotlib构建,能够用更简洁的代码生成更美观且具有统计意义的图表。
import seaborn as sns
# 设置Seaborn默认主题
sns.set_theme(style='whitegrid', font='SimHei')
# 创建示例数据
np.random.seed(42)
df_sample = pd.DataFrame({
'部门': np.random.choice(['开发部', '市场部', '销售部', '设计部'], 200),
'工作年限': np.random.randint(1, 15, 200),
'年薪(万元)': np.random.normal(5000, 1500, 200).astype(int),
'满意度': np.random.uniform(1, 5, 200).round(1)
})
# 1. 直方图 - 查看数据分布
plt.figure(figsize=(10, 6))
sns.histplot(data=df_sample, x='年薪(万元)', bins=20, kde=True, color='#3498db')
plt.title('年薪分布', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 2. 箱线图 - 比较各部门年薪分布
plt.figure(figsize=(10, 6))
sns.boxplot(data=df_sample, x='部门', y='年薪(万元)', palette='Set2')
plt.title('各部门年薪分布', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 3. 散点图 - 工作年限与年薪的关系
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df_sample, x='工作年限', y='年薪(万元)',
hue='部门', style='部门', s=80, alpha=0.7)
plt.title('工作年限 vs 年薪(按部门)', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 4. 热力图 - 相关性可视化
plt.figure(figsize=(8, 6))
numeric_cols = df_sample.select_dtypes(include=[np.number])
sns.heatmap(numeric_cols.corr(), annot=True, cmap='coolwarm',
center=0, fmt='.2f', linewidths=0.5)
plt.title('变量间相关性', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
5. 实战项目:用公开数据学习数据分析
让我们综合运用前面学到的所有内容,进行一个分析实际公开数据的迷你项目。本示例使用首尔市各区的虚构人口统计数据。实际的公开数据可以从韩国公共数据门户(data.go.kr)或首尔开放数据广场(data.seoul.go.kr)下载。
5.1 数据准备与探索
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 中文字体设置
plt.rcParams['font.family'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style='whitegrid', font='SimHei')
# 创建首尔市各区人口数据(虚构数据)
data = {
'区': ['江南区', '江东区', '江北区', '江西区', '冠岳区',
'广津区', '九老区', '衿川区', '芦原区', '道峰区',
'东大门区', '铜雀区', '麻浦区', '西大门区', '瑞草区',
'城东区', '城北区', '松坡区', '阳川区', '永登浦区',
'龙山区', '恩平区', '钟路区', '中区', '中浪区'],
'人口': [527641, 426067, 303065, 577586, 497702,
356174, 411084, 230696, 518922, 324686,
347780, 392163, 378476, 314109, 429154,
311024, 436592, 667483, 457806, 397789,
229431, 481623, 149479, 125177, 398690],
'户数': [231547, 183224, 143628, 258921, 247896,
161438, 189362, 115892, 214567, 137852,
168234, 177621, 184523, 148762, 185423,
145234, 193458, 278456, 186742, 189234,
113256, 201234, 75623, 65892, 172345],
'面积': [39.50, 24.59, 23.60, 41.44, 29.57,
17.06, 20.12, 13.01, 35.44, 20.70,
14.22, 16.35, 23.84, 17.61, 46.98,
16.85, 24.57, 33.87, 17.41, 24.55,
21.87, 29.71, 23.91, 9.96, 18.50],
'老年人口比例': [12.8, 14.2, 19.5, 13.6, 14.8,
13.1, 15.3, 13.9, 16.2, 18.7,
16.8, 14.1, 12.4, 16.1, 11.9,
13.5, 17.2, 11.8, 14.5, 14.9,
15.2, 16.4, 18.1, 17.5, 16.9]
}
df = pd.DataFrame(data)
# 基本探索
print("=== 数据基本信息 ===")
print(f"数据规模: {df.shape}")
print(f"\n数据类型:\n{df.dtypes}")
print(f"\n基本统计量:\n{df.describe()}")
print(f"\n前5行:\n{df.head()}")
5.2 创建派生变量与分析
# 计算人口密度(人口 / 面积)
df['人口密度'] = (df['人口'] / df['面积']).round(0).astype(int)
# 计算每户人口
df['每户人口'] = (df['人口'] / df['户数']).round(2)
# 人口前5名的区
top5_pop = df.nlargest(5, '人口')
print("=== 人口前5名的区 ===")
print(top5_pop[['区', '人口', '人口密度']])
# 人口密度前5名的区
top5_density = df.nlargest(5, '人口密度')
print("\n=== 人口密度前5名的区 ===")
print(top5_density[['区', '人口密度', '面积']])
# 按老年人口比例分类
df['老龄化阶段'] = pd.cut(df['老年人口比例'],
bins=[0, 7, 14, 20, 100],
labels=['未达老龄化社会', '老龄化社会',
'老龄社会', '超老龄社会'])
print("\n=== 各老龄化阶段的区数 ===")
print(df['老龄化阶段'].value_counts())
5.3 综合可视化
# 将4个图表放在一个Figure中
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
fig.suptitle('首尔市各区人口分析仪表板', fontsize=20, fontweight='bold', y=1.02)
# 1) 人口前10名的区 - 水平柱状图
top10 = df.nlargest(10, '人口')
axes[0, 0].barh(top10['区'], top10['人口'], color='#3498db')
axes[0, 0].set_title('人口前10名的区', fontsize=14, fontweight='bold')
axes[0, 0].set_xlabel('人口')
axes[0, 0].invert_yaxis()
# 2) 人口密度分布 - 直方图
axes[0, 1].hist(df['人口密度'], bins=10, color='#2ecc71', edgecolor='white')
axes[0, 1].set_title('人口密度分布', fontsize=14, fontweight='bold')
axes[0, 1].set_xlabel('人口密度(人/km2)')
axes[0, 1].set_ylabel('区数')
axes[0, 1].axvline(df['人口密度'].mean(), color='red', linestyle='--',
label=f'平均值: {df["人口密度"].mean():.0f}')
axes[0, 1].legend()
# 3) 面积 vs 人口散点图
scatter = axes[1, 0].scatter(df['面积'], df['人口'],
c=df['老年人口比例'], cmap='RdYlGn_r',
s=100, alpha=0.7, edgecolors='gray')
axes[1, 0].set_title('面积 vs 人口(颜色:老年人口比例)', fontsize=14, fontweight='bold')
axes[1, 0].set_xlabel('面积(km2)')
axes[1, 0].set_ylabel('人口')
plt.colorbar(scatter, ax=axes[1, 0], label='老年人口比例(%)')
# 4) 老龄化阶段分布 - 饼图
stage_counts = df['老龄化阶段'].value_counts()
axes[1, 1].pie(stage_counts.values, labels=stage_counts.index,
autopct='%1.1f%%', colors=['#2ecc71', '#f39c12', '#e74c3c'],
startangle=90, shadow=True)
axes[1, 1].set_title('各老龄化阶段的区分布', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig('seoul_population_dashboard.png', dpi=150, bbox_inches='tight')
plt.show()
print("\n分析完成!'seoul_population_dashboard.png'文件已保存。")
TIP:尝试从公共数据门户(data.go.kr)下载实际数据,替换上述代码中的虚构数据。很多数据以CSV格式提供,可以直接用
pd.read_csv()读取。
总结:下一步学习路线图
本文所涵盖的内容属于Python数据分析的基础。从环境搭建到使用Pandas进行数据操作、预处理,以及使用Matplotlib和Seaborn进行可视化,您已经体验了完整的数据分析工作流程。通过实战项目,您还看到了所有这些过程是如何相互连接的。
不要止步于此,以下是进入下一阶段的学习路线图建议。第一,深入学习NumPy。由于NumPy在Pandas内部运行,理解数组运算和向量化概念将帮助您编写更高效的代码。第二,通过Scikit-learn库学习机器学习基础。将回归分析、分类、聚类等基本算法应用到数据中,可以扩展到预测和模式发现的领域。第三,同时学习SQL基础。在实际工作中,大部分数据存储在数据库中,因此用SQL提取数据再用Pandas分析的工作流程非常常见。
此外,将学习范围扩展到Plotly和Bokeh等交互式可视化库、使用Streamlit或Dash构建数据仪表板,以及使用Apache Spark的PySpark处理大规模数据,您就能系统地培养数据分析师或数据科学家的能力。最重要的是持续实践。在公共数据门户、Kaggle等平台上找到感兴趣的数据集,亲自进行分析项目。您将获得仅靠理论无法掌握的实战技能。数据分析的世界广阔无垠,您的旅程才刚刚开始。