Python数据分析实践
准备工作
Anaconda的Jupyter Notebook以及Python 3.7进一步在Pycharm的编写运行
相关库
NumPy
- NumPy(Numerical Python) 是 Python 语言的一个扩展程序库
- 是一个运行速度非常快的数学库,主要用于数组计算,提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合
Pandans
Pandas 是基于Numpy的,很多功能都依赖于Numpy的ndarray实现的,使用pandas进行简单排序、分组、聚合等计算
- 它的使用基础是Numpy(提供高性能的矩阵运算)
- 用于数据挖掘和数据分析,同时也提供数据清洗功能
Matplotlib和Seaborn
- Matplotlib 是一个功能强大的数据可视化开源Python库
- Seaborn是一个Python数据可视化开源库,建立在matplotlib之上,并集成了pandas的数据结构
目标
主要是利用pandans进行数据清洗和预处理,使用相关分析方法包括:
1. 描述性分析(Descriptive Analysis)
定义:对数据的基本特征进行总结和描述,帮助理解数据的分布、趋势和集中/离散程度。
关键指标:
- 集中趋势:均值、中位数、众数。
- 离散程度:标准差、方差、极差(最大值-最小值)。
- 分布形态:偏度(对称性)、峰度(分布陡峭程度)。
- 数据分布:分位数(如四分位数)、频率分布表。
2. 相关性分析(Correlation Analysis)
定义:研究两个或多个变量之间的关联方向和强度,不区分因果关系。
关键方法:
- Pearson相关系数:衡量线性相关,范围[-1,1],0表示无线性相关。
- Spearman秩相关系数:衡量单调关系(非线性但趋势一致)。
- 热力图(Heatmap):可视化变量间的相关性矩阵。
应用场景: - 探索变量间的潜在关联(如广告投入与销量的关系)。
- 特征筛选(机器学习中去除高相关性的冗余特征)
3. 回归分析(Regression Analysis)
定义:建立变量间的数学模型,用于预测或解释因果关系。
主要类型:
线性回归:因变量(Y)与自变量(X)呈线性关系,如
Y = aX + b。- 关键指标:R²(模型解释力)、系数显著性(p-value)、残差分析。
- 逻辑回归:因变量为二分类(如是否购买),输出概率值。
- 多元回归:包含多个自变量(如预测房价时考虑面积、地段、房龄)。
应用场景: - 预测(如根据历史数据预测未来销售额)。
- 量化变量影响(如广告费每增加1万元,销量提升多少)。
最终实现Matplotlib和Seaborn的可视化显示。
Pandans
DataFrame
DataFrame用来处理结构化数据(SQL数据表,Excel表格)
df = pd.read_csv('data/movie.csv') # 加载movie.csv文件
df.head() # 展示前5条数据
df.columns #columns属性 获取DataFrame中的列名
df.dtypes #通过dtypes属性,或者info()方法获取数据类型
country_df = df['country'] #加载一列数据,通过df['列名']方式获取
df.loc[0] #获取第一行的数据,loc:通过行索引标签获取指定行数据
df.loc[[0, 99, 999]] #也可以通过索引标签获取多行数据
df.iloc[0] #iloc : 通过行号获取行数据
# iloc传入的是索引的序号,loc是索引的标签
df.shape # 打印行数和列数
df.size # 打印数据的个数’
df.ndim # 该数据集的维度获取指定行/列数据
loc和iloc属性既可以用于获取列数据,也可以用于获取行数据
- df.loc[[行],[列]]
- df.iloc[[行],[列]]
使用 loc 获取数据中的1列/几列
- df.loc[[所有行],[列名]]
- 取出所有行,可以使用切片语法 df.loc[ : , [列名]]
subset = df.loc[:,['year','pop']]
subset = df.iloc[:,[2,4,-1]] # 列序号可以使用-1代表最后一列
# loc 只能接受行/列 的名字, 不能传入索引
# iloc只能接受行/列的索引,不能传入行名,或者列名Series
Series用来处理单列数据,也可以把DataFrame看作由Series对象组成的字典或集合
在Pandas中,Series是一维容器,Series表示DataFrame的每一列
- 可以把DataFrame看作由Series对象组成的字典,其中key是列名,值是Series
- Series和Python中的列表非常相似,但是它的每个元素的数据类型必须相同
s1 = pd.Series(['banana',42])
s2 = pd.Series(['Wes McKinney','Male'],index = ['Name','Gender'])
print(s1)
print(s2)
输出结果
0 banana
1 42
dtype: object
Name Wes McKinney
Gender Male
dtype: object常用属性
Series的一些属性
属性 说明 loc 使用索引值取子集 iloc 使用索引位置取子集 dtype或dtypes Series内容的类型 T Series的转置矩阵 shape 数据的维数 size Series中元素的数量 values Series的值
常用方法
Series的一些方法
| 方法 | 说明 |
|---|---|
| append | 连接两个或多个Series |
| corr | 计算与另一个Series的相关系数 |
| cov | 计算与另一个Series的协方差 |
| describe | 计算常见统计量 |
| drop_duplicates | 返回去重之后的Series |
| equals | 判断两个Series是否相同 |
| get_values | 获取Series的值,作用与values属性相同 |
| hist | 绘制直方图 |
| isin | Series中是否包含某些值 |
| min | 返回最小值 |
| max | 返回最大值 |
| mean | 返回算术平均值 |
| median | 返回中位数 |
| mode | 返回众数 |
| quantile | 返回指定位置的分位数 |
| replace | 用指定值代替Series中的值 |
| sample | 返回Series的随机采样值 |
| sort_values | 对值进行排序 |
| to_frame | 把Series转换为DataFrame |
| unique | 去重返回数组 |
数据组合
在动手进行数据分析工作之前,需要进行数据清理工作,数据清理的主要目标是
- 每个观测值成一行
- 每个变量成一列
- 每种观测单元构成一张表格
concat组合
import pandas as pd
df1 = pd.read_csv('data/concat_1.csv')
df2 = pd.read_csv('data/concat_2.csv')
df3 = pd.read_csv('data/concat_3.csv')
row_concat = pd.concat([df1,df2,df3]) # 将3个DataFrame放到同一个列表中join合并
可以是依据两个DataFrame的行索引,或者一个DataFrame的行索引另一个DataFrame的列索引进行数据合并
merge组合
水平连接两个DataFrame对象
concat, join, 和merge的区别
concat:
- Pandas函数
- 可以垂直和水平地连接两个或多个pandas对象
- 只用索引对齐
- 默认是外连接(也可以设为内连接)
join:
- DataFrame方法
- 只能水平连接两个或多个pandas对象
- 对齐是靠被调用的DataFrame的列索引或行索引和另一个对象的行索引(不能是列索引)
- 通过笛卡尔积处理重复的索引值
- 默认是左连接(也可以设为内连接、外连接和右连接)
merge:
- DataFrame方法
- 只能水平连接两个DataFrame对象
- 对齐是靠被调用的DataFrame的列或行索引和另一个DataFrame的列或行索引
- 通过笛卡尔积处理重复的索引值
- 默认是内连接(也可以设为左连接、外连接、右连接)
缺失数据处理
好多数据集都含缺失数据。缺失数据有多重表现形式
- 数据库中,缺失数据表示为NULL
- 在某些编程语言中用NA表示
- 缺失值也可能是空字符串(‘ ’)或数值
- 在Pandas中使用NaN表示缺失值
Pandas中的NaN值来自NumPy库,NumPy中缺失值有几种表示形式:NaN,NAN,nan,他们都一样
缺失值和其它类型的数据不同,它毫无意义,NaN不等于0,也不等于空串,
Pandas提供了pd.isnull(NaN),pd.notnull(NaN)
pandas在加载数据可以选择是否包含缺少数据
#提供了两个方法keep_default_na 与 na_values
pd.read_csv('data/survey_visited.csv',na_values=[""],keep_default_na = False)
#keep_default_na加载数据,不包含默认缺失值,na_values=[""]手动指定缺失值使用Missingno 库对缺失值进行可视化
import missingno as msno
msno.bar(train) #条形图提供了数据集完整性的可视化图形
msno.matrix(train) #提供了快速直观的查看缺失值的分布情况
msno.heatmap(train) #查看缺失值之间是否具有相关性
缺失值处理
- 删除缺失值:删除缺失值会损失信息,并不推荐删除,当缺失数据占比较低的时候,可以尝试使用删除缺失值
填充缺失值(非时间序列数据):填充缺失值是指用一个估算的值来去替代缺失数
- 使用常量来替换(默认值)
- 使用统计量替换(缺失值所处列的平均值、中位数、众数)
- 算法来填充缺失值的方法
整理数据
melt函数
pandas的melt函数可以把宽数据集,转换为长数据集
stack函数
返回一个具有多层级索引的数据,配合reset_index可以实现宽数据变成长数据
state_fruit = pd.read_csv('data/state_fruit.csv', index_col=0)
state_fruit.stack()Texas Apple 12
Orange 10
Banana 40
Arizona Apple 9
Orange 7
Banana 12
Florida Apple 0
Orange 14
Banana 190
dtype: int64wide_to_long函数的用法
处理列名带数字后缀的宽数据
数据分组
print(df.groupby('year')['lifeExp'].mean())
print(df.groupby(['year', 'continent'])[['lifeExp','gdpPercap']].mean()) #多组也可以
df.groupby('continent')['country'].nunique() #使用 nunique 方法 计算Pandas Series的唯一值计数通过df.groupby('year')先创一个分组对象,从分组之后的数据DataFrameGroupBy中,传入lifeExp数据,求平均
Pandas内置的聚合方法
可以与groupby一起使用的方法和函数
| Pandas方法 | Numpy函数 | 说明 |
|---|---|---|
| count | np.count_nonzero | 频率统计(不包含NaN值) |
| size | 频率统计(包含NaN值) | |
| mean | np.mean | 求平均值 |
| std | np.std | 标准差 |
| min | np.min | 最小值 |
| quantile() | np.percentile() | 分位数 |
| max | np.max | 求最大值 |
| sum | np.sum | 求和 |
| var | np.var | 方差 |
| describe | 计数、平均值、标准差,最小值、分位数、最大值 | |
| first | 返回第一行 | |
| last | 返回最后一行 | |
| nth | 返回第N行(Python从0开始计数) |
transform 转换,需要把DataFrame中的值传递给一个函数, 而后由该函数"转换"数据。
# 计算z-score x - 平均值/标准差
def my_zscore(x):
return (x-x.mean())/x.std()
#按年分组 计算z-score
df.groupby('year').lifeExp.transform(my_zscore)filter 方法,传入一个返回布尔值的函数,返回False的数据会被过滤掉
tips_filtered = tips.groupby('size').filter(lambda x: x['size'].count()>30)tips.groupby('size'):按照 size 这一列的值对 tips 数据框进行分组。
.filter(lambda x: x['size'].count() > 30):
- 这个
lambda函数的x代表的是size分组后的每个子 DataFrame。 x['size'].count()计算该组的行数(即这一组的样本数量)。
实践应用
本次实践的任务是:对电影数据集进行了可视化分析,从庞大的数据集中筛选可用信息,探索了电影预算、票房、评分和类型等因素之间的关系。通过多种可视化方法,我们能够直观地理解电影行业的一些关键特征和趋势。
数据概况分析
数据行/列数量
import pandas as pd # 数据处理库
import numpy as np # 数值计算库
import matplotlib.pyplot as plt # 基础可视化库
import seaborn as sns # 高级可视化库
file_path = 'data/movie.csv'
# 读取数据
df = pd.read_csv(file_path)
df.head() # 查看数据前5行
df.shape #查看数据维度
print(df.info()) # 数据的基本信息缺失值分布
# 检查缺失值
#print("\n各列缺失值数量:")
#missing_values = df.isnull().sum()
#print(missing_values)
print(f"\n缺失值比例最高的前5个特征:")
missing_percentage = (missing_values / len(df) * 100).sort_values(ascending=False)
missing_percentage.head()
数据清洗和预处理
使用Z分数处理异常值


通常情况下,如果 ∣𝑍∣>3∣Z∣>3(即Z分数的绝对值大于3),则认为该数据点是异常值。这是因为在正态分布中,数据点落在平均值±3个标准差之外的概率非常小(约为0.27%)。
# 处理缺失值
# 对于数值型列,用中位数填充缺失值
numeric_cols = df.select_dtypes(include=['float64', 'int64']).columns
for col in numeric_cols:
if df[col].isnull().sum() > 0:
median_value = df[col].median() #中位数填充缺失值
df[col] = df[col].fillna(median_value)
print(f"列 '{col}' 的缺失值已用中位数 {median_value} 填充")
# 对于分类特征,用最常见值填充
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
if df[col].isnull().sum() > 0:
mode_value = df[col].mode()[0]
df[col] = df[col].fillna(mode_value)
print(f"列 '{col}' 的缺失值已用众数填充")
# 处理异常值(使用Z分数方法识别异常值)
def handle_outliers(df, column):
"""使用Z分数方法处理异常值,将|z|>3的值替换为上下限"""
if df[column].dtype in ['int64', 'float64']:
z_scores = stats.zscore(df[column].dropna()) #计算z分数
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3)
upper_limit = df[column].mean() + 3 * df[column].std()
lower_limit = df[column].mean() - 3 * df[column].std()
outliers_count = len(df[column]) - filtered_entries.sum()
if outliers_count > 0:
print(f"列 '{column}' 中发现 {outliers_count} 个异常值")
df[column] = np.where(df[column] > upper_limit, upper_limit,
np.where(df[column] < lower_limit, lower_limit, df[column]))
print(f"已将异常值替换为上限 {upper_limit:.2f} 或下限 {lower_limit:.2f}")
# 处理重要的数值列中的异常值
important_numeric_cols = ['budget', 'gross', 'imdb_score', 'duration']
for col in important_numeric_cols:
handle_outliers(df, col)
# 确保年份不为0(如果为0,则用中位数填充)
df.loc[df['title_year'] == 0, 'title_year'] = df['title_year'].median()
print(f"年份为0的记录已用中位数 {df['title_year'].median()} 填充")
分析方法
分析工具库
from scipy import stats # 统计分析库
from sklearn.linear_model import LinearRegression # 线性回归模型
from sklearn.model_selection import train_test_split # 数据集拆分工具
from sklearn.metrics import mean_squared_error, r2_score # 模型评估指标SciPy stats:提供了大量统计函数,包括概率分布、统计测试、描述性统计等。在数据分析中常用于假设检验、计算Z分数等统计操作。
Scikit-learn:机器学习库,提供了各种算法实现:
- LinearRegression:实现线性回归算法
- train_test_split:将数据集分为训练集和测试集
- mean_squared_error和r2_score:评估模型性能的指标
描述性统计分析
# 计算关键统计指标
# 分析类型分布
# 将流派字符串拆分为列表
#if not isinstance(df['genres'].iloc[0], list): # 检查是否已经拆分
# df['genres'] = df['genres'].str.split('|')
# 计算每个类型的电影数量
#genres_exploded = df['genres'].explode()
#genre_counts = genres_exploded.value_counts()
#print("\n各类型电影数量:")
#print(genre_counts)
# 分析评分分布
rating_stats = df['imdb_score'].describe() #describe()提供各项数据
print("\nIMDB评分分布统计:")
print(rating_stats)
print(f"\n评分超过7.5的电影比例: {(df['imdb_score'] > 7.5).mean()*100:.2f}%")
相关性分析
.corr()
皮尔逊相关系数的取值范围是 [-1, 1]:
- 1:完全正相关(A 增大,B 也增大)。
- 0:无相关性。
- -1:完全负相关(A 增大,B 反向减少)。
# 选择关键数值特征进行相关性分析
correlation_cols = ['budget', 'gross', 'imdb_score', 'duration',
'director_facebook_likes', 'actor_1_facebook_likes',
'num_voted_users', 'num_critic_for_reviews']
# 计算相关系数矩阵
correlation_matrix = df[correlation_cols].corr() #计算各列之间的相关系数
print("\n相关系数矩阵:")
print(correlation_matrix)
# 计算投资回报率
df['roi'] = (df['gross'] - df['budget']) / df['budget']
# 移除异常的ROI值(可能因预算值很小导致极端ROI)
df['roi'] = df['roi'].clip(lower=-1, upper=10)
# 分析预算与总收入的关系
budget_gross_corr = df['budget'].corr(df['gross'])
print(f"\n预算与票房收入相关系数: {budget_gross_corr:.3f}")
# 分析评分与票房的关系
score_gross_corr = df['imdb_score'].corr(df['gross'])
print(f"评分与票房收入相关系数: {score_gross_corr:.3f}")

回归分析(参考)
X:选取 4 个特征(自变量):
imdb_score(IMDb 评分)budget(制作预算)duration(电影时长)actor_1_facebook_likes(主演 1 的 Facebook 点赞数,表示演员的热度)
y:目标变量(因变量),即 票房 gross。
train_test_split() 作用:
- 80% 数据用于训练 (
X_train, y_train)。 - 20% 数据用于测试 (
X_test, y_test)。 random_state=42保证随机性固定,便于复现实验结果。
均方误差(MSE, Mean Squared Error)
均方误差(MSE)衡量预测误差的大小,值越小越好。
$$ MSE= \frac{1}{n} \sum (y_{\text{true}} - y_{\text{pred}})^2 $$
决定系数(R², R-squared)
决定系数(R²)衡量模型的解释能力,其计算公式如下:
$$ R2= 1 - \frac{\sum (y_{\text{true}} - y_{\text{pred}})^2}{\sum (y_{\text{true}} - \bar{y})^2} $$
- R² = 1:模型完美拟合数据。
- R² = 0:模型没有任何预测能力,相当于简单地用均值预测。
- R² < 0:模型甚至比均值预测还要差。
# 使用评分、预算和其他特征预测票房
X = df[['imdb_score', 'budget', 'duration', 'actor_1_facebook_likes']].copy()
y = df['gross']
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建并训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = model.predict(X_test)
# 评估模型
mse = mean_squared_error(y_test, y_pred) #均方误差
r2 = r2_score(y_test, y_pred) #决定系数
print("\n回归模型结果:")
print(f"均方误差(MSE): {mse:.2f}")
print(f"决定系数(R²): {r2:.3f}")
print("特征系数:")
for feature, coef in zip(X.columns, model.coef_):
print(f"- {feature}: {coef:.2f}")
可视化
# 1. 电影评分直方图
plt.figure(figsize=(10, 6))
sns.histplot(df['imdb_score'], kde=True, bins=20, color='skyblue')
plt.title('IMDB评分分布图')
plt.xlabel('IMDB评分')
plt.ylabel('电影数量')
plt.axvline(df['imdb_score'].mean(), color='red', linestyle='--', label=f'平均分: {df["imdb_score"].mean():.2f}')
plt.legend()
plt.show()
# 2. 年份与电影数量趋势图
df_by_year = df.groupby('title_year').size().reset_index(name='count')
plt.figure(figsize=(12, 6))
sns.lineplot(x='title_year', y='count', data=df_by_year[df_by_year['title_year'] > 1950], marker='o')
plt.title('不同年份电影发行数量')
plt.xlabel('年份')
plt.ylabel('电影数量')
plt.grid(True)
plt.show()