Python数据分析实践

准备工作

Anaconda的Jupyter Notebook以及Python 3.7进一步在Pycharm的编写运行

相关库

NumPy

  • NumPy(Numerical Python) 是 Python 语言的一个扩展程序库
  • 是一个运行速度非常快的数学库,主要用于数组计算,提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合

Pandans

Pandas 是基于Numpy的,很多功能都依赖于Numpy的ndarray实现的,使用pandas进行简单排序、分组、聚合等计算

  • 它的使用基础是Numpy(提供高性能的矩阵运算)
  • 用于数据挖掘和数据分析,同时也提供数据清洗功能

MatplotlibSeaborn

  • 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万元,销量提升多少)。

最终实现MatplotlibSeaborn的可视化显示。

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或dtypesSeries内容的类型
    TSeries的转置矩阵
    shape数据的维数
    sizeSeries中元素的数量
    valuesSeries的值

常用方法

Series的一些方法

方法说明
append连接两个或多个Series
corr计算与另一个Series的相关系数
cov计算与另一个Series的协方差
describe计算常见统计量
drop_duplicates返回去重之后的Series
equals判断两个Series是否相同
get_values获取Series的值,作用与values属性相同
hist绘制直方图
isinSeries中是否包含某些值
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) #查看缺失值之间是否具有相关性

image

缺失值处理

  • 删除缺失值:删除缺失值会损失信息,并不推荐删除,当缺失数据占比较低的时候,可以尝试使用删除缺失值
  • 填充缺失值(非时间序列数据):填充缺失值是指用一个估算的值来去替代缺失数

    • 使用常量来替换(默认值)
    • 使用统计量替换(缺失值所处列的平均值、中位数、众数)
  • 算法来填充缺失值的方法

整理数据

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: int64

wide_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函数说明
countnp.count_nonzero频率统计(不包含NaN值)
size 频率统计(包含NaN值)
meannp.mean求平均值
stdnp.std标准差
minnp.min最小值
quantile()np.percentile()分位数
maxnp.max求最大值
sumnp.sum求和
varnp.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()

image

数据清洗和预处理

使用Z分数处理异常值

image

image

通常情况下,如果 ∣𝑍∣>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()} 填充")

image

分析方法

分析工具库

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}%")

image

相关性分析

.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}")

image

image

回归分析(参考)

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}")

image

可视化

# 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()

image

# 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()

image