数据分析全流程实战:Python(Pandas/Matplotlib/Numpy)+ Oracle(附可下载数据源+多图形绘制)

本文聚焦数据分析领域Python生态与Oracle的核心对比,选用可公开下载的「全球咖啡产销数据集」(区别于之前的收入数据),覆盖数据导入、清洗、分析、可视化全流程,结合代码示例与多类型图形绘制,适配数据分析岗位面试/实战需求。

一、前期准备:数据源与环境配置

1. 数据源选择(可公开下载)

选用「2010-2023年全球咖啡产销数据集」(可从以下渠道下载):

  • 官方渠道:国际咖啡组织(ICO)官网
  • 开源平台:Kaggle(搜索“Global Coffee Production & Consumption”)
    核心字段(CSV格式):
    | 字段名 | 说明 |
    |--------|------|
    | year | 年份(2010-2023) |
    | country | 咖啡主产国(巴西、哥伦比亚、越南、埃塞俄比亚、印尼) |
    | production | 咖啡产量(千袋,1袋=60kg) |
    | consumption | 咖啡消费量(千袋) |
    | export | 咖啡出口量(千袋) |
    | price | 咖啡均价(美元/磅) |

2. 环境配置

# 安装依赖包(Oracle连接需额外安装cx_Oracle)
pip install pandas numpy matplotlib cx_Oracle sqlalchemy

注意:Oracle连接需配置客户端(Instant Client),参考Oracle官方文档完成环境变量配置。

3. 数据导入Oracle(为后续对比做准备)

import pandas as pd
from sqlalchemy import create_engine

# 1. 读取本地CSV数据(数据源下载后放在本地)
df = pd.read_csv("coffee_data.csv", encoding="utf-8")

# 2. 连接Oracle(格式:oracle+cx_oracle://用户名:密码@主机:端口/服务名)
# 示例:本地Oracle数据库,服务名ORCL,用户名scott,密码tiger
engine = create_engine("oracle+cx_oracle://scott:tiger@localhost:1521/ORCL")

# 3. 将数据写入Oracle表:coffee_table(Oracle表名建议大写)
df.to_sql(name="COFFEE_TABLE", con=engine, if_exists="replace", index=False)

print("数据成功导入Oracle!")

二、核心对比:Python vs Oracle(数据处理+分析)

维度1:数据查询与基础统计

分析需求 Oracle实现 Python(Pandas/Numpy)实现 核心差异
1. 查询2018-2023年巴西咖啡产销数据 ```sql
SELECT YEAR, PRODUCTION, CONSUMPTION
FROM COFFEE_TABLE
WHERE COUNTRY = ‘Brazil’ AND YEAR BETWEEN 2018 AND 2023;
``` ```python

读取Oracle数据到DataFrame

df = pd.read_sql(“SELECT * FROM COFFEE_TABLE”, engine)

筛选数据(注意Oracle字段名大写,Pandas读取后默认小写)

df_brazil_2018_2023 = df[(df[“country”] == “Brazil”) & (df[“year”] >= 2018) & (df[“year”] <= 2023)][[“year”, “production”, “consumption”]]
print(df_brazil_2018_2023)

| 2. 计算各产国咖啡产量均值/最大值 | ```sql
SELECT 
  COUNTRY,
  AVG(PRODUCTION) AS PROD_AVG,
  MAX(PRODUCTION) AS PROD_MAX
FROM COFFEE_TABLE
GROUP BY COUNTRY;
```| ```python
# 按国家分组计算统计值
prod_stats = df.groupby("country")["production"].agg(["mean", "max"])
prod_stats.columns = ["prod_avg", "prod_max"]
# 保留2位小数
prod_stats = prod_stats.round(2)
print(prod_stats)
```| Oracle需显式GROUP BY,Pandas的groupby+agg更简洁,支持批量聚合 |
| 3. 计算各年份咖啡出口率(出口量/产量) | ```sql
SELECT 
  YEAR,
  COUNTRY,
  ROUND((EXPORT / PRODUCTION) * 100, 2) AS EXPORT_RATE
FROM COFFEE_TABLE
WHERE PRODUCTION > 0;
```| ```python
# 新增列计算出口率(避免除零错误)
df["export_rate"] = np.where(df["production"] > 0, (df["export"] / df["production"]) * 100, 0)
df["export_rate"] = df["export_rate"].round(2)
print(df[["year", "country", "export_rate"]])
```| Python的np.where可直接处理异常值(除零),Oracle需额外判断 |

### 维度2:数据清洗(缺失值/异常值/重复值)
| 清洗需求 | Oracle实现 | Python(Pandas/Numpy)实现 | 核心差异 |
|----------|-----------|---------------------|----------|
| 1. 处理缺失值(填充各国家产量均值) | ```sql
-- Oracle需分国家更新,步骤繁琐
UPDATE COFFEE_TABLE t1
SET PRODUCTION = (SELECT AVG(PRODUCTION) FROM COFFEE_TABLE t2 WHERE t2.COUNTRY = t1.COUNTRY)
WHERE PRODUCTION IS NULL;
```| ```python
# 按国家分组填充缺失值(一行搞定)
df["production"] = df.groupby("country")["production"].transform(lambda x: x.fillna(x.mean()))
df["consumption"] = df.groupby("country")["consumption"].transform(lambda x: x.fillna(x.mean()))
```| Python支持分组填充,Oracle需嵌套子查询,逻辑复杂 |
| 2. 识别异常值(IQR四分位法) | ```sql
-- Oracle需多步计算四分位数,逻辑极复杂
WITH stats AS (
  SELECT 
    COUNTRY,
    PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY PRODUCTION) AS Q1,
    PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY PRODUCTION) AS Q3
  FROM COFFEE_TABLE
  GROUP BY COUNTRY
)
SELECT t.* 
FROM COFFEE_TABLE t
JOIN stats s ON t.COUNTRY = s.COUNTRY
WHERE t.PRODUCTION < (s.Q1 - 1.5*(s.Q3 - s.Q1)) OR t.PRODUCTION > (s.Q3 + 1.5*(s.Q3 - s.Q1));
```| ```python
# IQR法识别异常值
def detect_outliers_by_country(df, col):
    outliers = pd.DataFrame()
    for country in df["country"].unique():
        country_data = df[df["country"] == country][col]
        q1 = np.percentile(country_data, 25)
        q3 = np.percentile(country_data, 75)
        iqr = q3 - q1
        lower_bound = q1 - 1.5 * iqr
        upper_bound = q3 + 1.5 * iqr
        country_outliers = df[(df["country"] == country) & ((df[col] < lower_bound) | (df[col] > upper_bound))]
        outliers = pd.concat([outliers, country_outliers])
    return outliers

prod_outliers = detect_outliers_by_country(df, "production")
print(f"产量异常值:\n{prod_outliers[['country', 'year', 'production']]}")
```| Python支持自定义函数+循环分组处理,Oracle需CTE+百分位函数,门槛高 |
| 3. 去重(按国家+年份去重) | ```sql
DELETE FROM COFFEE_TABLE
WHERE ROWID NOT IN (
  SELECT MIN(ROWID) FROM COFFEE_TABLE GROUP BY COUNTRY, YEAR
);
```| ```python
# 按国家+年份去重,保留第一条
df = df.drop_duplicates(subset=["country", "year"], keep="first")
```| Python的drop_duplicates简洁高效,Oracle需借助ROWID |

## 三、Matplotlib可视化实战(多图形绘制)
基于清洗后的DataFrame,绘制7类核心分析图形,覆盖咖啡产销分析全场景:

### 1. 折线图:巴西咖啡产量/价格趋势(2010-2023)
```python
import matplotlib.pyplot as plt

# 设置中文字体(避免乱码)
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

# 筛选巴西数据
df_brazil = df[df["country"] == "Brazil"]

# 创建双轴图(产量+价格)
fig, ax1 = plt.subplots(figsize=(12, 6))

# 左轴:产量
ax1.plot(df_brazil["year"], df_brazil["production"], color="darkgreen", marker="o", label="产量(千袋)")
ax1.set_xlabel("年份")
ax1.set_ylabel("产量(千袋)", color="darkgreen")
ax1.tick_params(axis="y", labelcolor="darkgreen")
ax1.grid(True, alpha=0.3)

# 右轴:价格
ax2 = ax1.twinx()
ax2.plot(df_brazil["year"], df_brazil["price"], color="darkred", marker="s", label="均价(美元/磅)")
ax2.set_ylabel("均价(美元/磅)", color="darkred")
ax2.tick_params(axis="y", labelcolor="darkred")

# 合并图例
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")

plt.title("2010-2023年巴西咖啡产量与价格趋势")
plt.savefig("brazil_coffee_trend.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:双轴折线图清晰展示产量与价格的反向趋势(产量高时价格低),是大宗商品分析的经典图形。

2. 分组柱状图:各产国2023年产销对比

# 筛选2023年数据
df_2023 = df[df["year"] == 2023]

# 创建画布
plt.figure(figsize=(14, 7))

# 柱状图位置
x = np.arange(len(df_2023["country"]))
width = 0.35

# 绘制柱状图
plt.bar(x - width/2, df_2023["production"], width, label="产量", color="forestgreen")
plt.bar(x + width/2, df_2023["consumption"], width, label="消费量", color="firebrick")

# 美化
plt.xlabel("产国")
plt.ylabel("数量(千袋)")
plt.title("2023年全球主要咖啡产国产销对比")
plt.xticks(x, df_2023["country"], rotation=15)
plt.legend()
plt.grid(axis="y", alpha=0.3)

plt.savefig("2023_coffee_prod_cons.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:分组柱状图直观对比各产国产销差异,可看到巴西产量远高于消费量(主要用于出口)。

3. 堆叠面积图:各产国出口量占比趋势

# 按年份+国家汇总出口量
df_export_pivot = df.pivot_table(index="year", columns="country", values="export", fill_value=0)

# 绘制堆叠面积图
plt.figure(figsize=(12, 7))
df_export_pivot.plot.area(alpha=0.7, ax=plt.gca())

plt.xlabel("年份")
plt.ylabel("出口量(千袋)")
plt.title("2010-2023年全球咖啡出口量占比趋势")
plt.legend(title="产国", loc="upper left")
plt.grid(True, alpha=0.3)

plt.savefig("coffee_export_area.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:堆叠面积图展示各产国出口量的贡献占比,可看到巴西始终是最大出口国,越南出口占比逐年提升。

4. 散点图:咖啡产量与价格的相关性

plt.figure(figsize=(10, 6))

# 按国家区分颜色
countries = df["country"].unique()
colors = ["green", "red", "blue", "orange", "purple"]
color_map = dict(zip(countries, colors))

# 绘制散点图
for country in countries:
    df_country = df[df["country"] == country]
    plt.scatter(df_country["production"], df_country["price"], 
                label=country, color=color_map[country], s=60, alpha=0.8)

# 添加趋势线
from scipy import stats
slope, intercept, r_value, p_value, std_err = stats.linregress(df["production"], df["price"])
plt.plot(df["production"], intercept + slope * df["production"], "k--", label=f"趋势线 (r={r_value:.2f})")

plt.xlabel("产量(千袋)")
plt.ylabel("均价(美元/磅)")
plt.title("咖啡产量与价格相关性分析")
plt.legend()
plt.grid(True, alpha=0.3)

plt.savefig("coffee_prod_price_corr.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:散点图+趋势线展示产量与价格的负相关(r≈-0.7),验证“供大于求价格下跌”的经济学规律。

5. 饼图:2023年各产国咖啡产量占比

# 2023年产量汇总
df_2023_prod = df_2023.groupby("country")["production"].sum()

# 绘制饼图
plt.figure(figsize=(10, 10))
explode = (0.05, 0, 0, 0, 0)  # 突出巴西
plt.pie(df_2023_prod, explode=explode, labels=df_2023_prod.index, autopct="%1.1f%%",
        colors=colors, shadow=True, startangle=90)
plt.axis("equal")  # 保证正圆
plt.title("2023年全球咖啡产量占比")

plt.savefig("2023_coffee_prod_pie.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:饼图展示2023年产量结构,巴西占比42.5%,稳居全球第一。

6. 箱线图:各产国咖啡价格分布

plt.figure(figsize=(12, 7))

# 提取价格数据
price_data = [df[df["country"] == c]["price"] for c in countries]

# 绘制箱线图
box_plot = plt.boxplot(price_data, labels=countries, patch_artist=True)

# 填充颜色
for patch, color in zip(box_plot["boxes"], colors):
    patch.set_facecolor(color)
    patch.set_alpha(0.7)

plt.xlabel("产国")
plt.ylabel("价格(美元/磅)")
plt.title("各产国咖啡价格分布(2010-2023)")
plt.grid(axis="y", alpha=0.3)

plt.savefig("coffee_price_box.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:箱线图展示价格的中位数、四分位和异常值,埃塞俄比亚咖啡价格波动最大(品质差异大)。

7. 热力图:咖啡产销指标相关性矩阵

import seaborn as sns  # 辅助绘制热力图(需安装:pip install seaborn)

# 选择数值型字段计算相关性
corr_cols = ["production", "consumption", "export", "price", "export_rate"]
corr_matrix = df[corr_cols].corr().round(2)

# 绘制热力图
plt.figure(figsize=(9, 7))
sns.heatmap(corr_matrix, annot=True, cmap="RdYlGn", vmin=-1, vmax=1, linewidths=0.5)
plt.title("咖啡产销指标相关性热力图")

plt.savefig("coffee_corr_heatmap.png", dpi=300, bbox_inches="tight")
plt.show()

图形说明:热力图直观展示指标间的相关性,出口量与产量正相关(0.89),价格与产量负相关(-0.71)。

四、核心对比总结(面试必答)

工具/场景 Python(Pandas/Numpy/Matplotlib) Oracle
数据查询 灵活,支持多维度筛选/分组,兼容Oracle语法 高效,擅长海量数据的结构化查询(WHERE/GROUP BY)
数据清洗 一站式解决(缺失值/异常值/重复值),语法简洁 需嵌套复杂SQL,分组清洗步骤繁琐
统计分析 支持自定义函数、向量化运算,效率高 仅支持内置聚合函数,复杂统计需多步查询
可视化 直接绘制多类型图形(折线/柱状/热力图等) 无可视化能力,需导出数据到Python/Excel处理
适用场景 数据分析全流程(清洗→分析→可视化→报告) 数据存储、海量数据基础查询、事务性操作

五、面试高频追问与解答

  1. Q:数据分析中Oracle和Python的分工是什么?
    A:Oracle作为企业级数据库,负责海量咖啡产销数据的存储、结构化查询和事务管理;Python(Pandas/Numpy)负责复杂数据清洗、多维度统计分析,Matplotlib负责可视化呈现分析结果,两者结合可兼顾效率与灵活性。

  2. Q:为什么Oracle处理缺失值不如Python高效?
    A:Oracle需为每个分组(如咖啡产国)编写嵌套更新语句,逻辑冗余;而Python的groupby+transform+fillna可一键完成分组填充,且支持异常值(如除零)的快速处理,代码量仅为Oracle的1/5。

  3. Q:针对咖啡这类时间序列数据,Python有哪些优化分析的技巧?
    A:① 用Pandas的pivot_table重构时间序列数据;② 用rolling做移动平均分析价格趋势;③ 用双轴图对比产量与价格的反向关系,更贴合大宗商品分析场景。

六、完整代码整合(可直接运行)

# 导入依赖
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from sqlalchemy import create_engine

# 全局配置
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

# 1. 数据导入与读取
# 连接Oracle
engine = create_engine("oracle+cx_oracle://scott:tiger@localhost:1521/ORCL")
# 读取数据
df = pd.read_sql("SELECT * FROM COFFEE_TABLE", engine)

# 2. 数据清洗
# 填充缺失值(按国家分组)
df["production"] = df.groupby("country")["production"].transform(lambda x: x.fillna(x.mean()))
df["consumption"] = df.groupby("country")["consumption"].transform(lambda x: x.fillna(x.mean()))
# 计算出口率(避免除零)
df["export_rate"] = np.where(df["production"] > 0, (df["export"] / df["production"]) * 100, 0)
df["export_rate"] = df["export_rate"].round(2)
# 去重
df = df.drop_duplicates(subset=["country", "year"], keep="first")

# 3. 基础统计分析
prod_stats = df.groupby("country")["production"].agg(["mean", "max"]).round(2)
prod_stats.columns = ["prod_avg", "prod_max"]
print("各产国产量统计:\n", prod_stats)

# 4. 可视化(按需选择图形运行)
# 4.1 巴西产量价格趋势
df_brazil = df[df["country"] == "Brazil"]
fig, ax1 = plt.subplots(figsize=(12, 6))
ax1.plot(df_brazil["year"], df_brazil["production"], color="darkgreen", marker="o", label="产量(千袋)")
ax1.set_xlabel("年份")
ax1.set_ylabel("产量(千袋)", color="darkgreen")
ax1.tick_params(axis="y", labelcolor="darkgreen")
ax1.grid(True, alpha=0.3)
ax2 = ax1.twinx()
ax2.plot(df_brazil["year"], df_brazil["price"], color="darkred", marker="s", label="均价(美元/磅)")
ax2.set_ylabel("均价(美元/磅)", color="darkred")
ax2.tick_params(axis="y", labelcolor="darkred")
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")
plt.title("2010-2023年巴西咖啡产量与价格趋势")
plt.savefig("brazil_coffee_trend.png", dpi=300, bbox_inches="tight")
plt.show()

# 其他图形代码可按需添加...

总结

  1. Python生态是数据分析的核心工具,覆盖清洗、分析、可视化全流程,Oracle仅作为数据存储和基础查询的支撑;
  2. 咖啡产销数据分析需结合业务场景选图形:趋势用折线图、对比用柱状图、分布用箱线图、相关性用热力图;
  3. 实战中需发挥Oracle的查询效率优势,再用Python完成复杂分析,两者分工协作可最大化提升分析效率。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐