解锁县域经济密码:用Python深度挖掘二十年发展轨迹

如果你手头有一份覆盖全国两千多个县域、横跨二十余年的经济统计年鉴数据,你会从何入手?是盯着密密麻麻的Excel表格望而却步,还是能迅速从中提炼出有洞察力的趋势和模式?对于区域经济研究者、政策分析师或是数据科学从业者而言,这类面板数据是一座富矿,但开采的难度同样不小。数据字段繁杂、单位不一、存在大量缺失值,传统的分析方法往往耗时费力,且难以从宏观视角把握微观单元的演变脉络。

今天,我们就来聊聊如何用Python,特别是pandasmatplotlibseaborn以及geopandas等工具链,系统性地对这类县域经济数据进行清洗、探索、分析和可视化。我们的目标不是简单地复现几个图表,而是构建一套可复用的分析框架,让你能高效地从数据中回答诸如“哪些县域实现了跨越式发展?”、“产业结构变迁有何地理规律?”、“公共服务投入与经济增长关系如何?”等具体问题。整个过程,我们将以一份模拟1997-2018年县域统计数据为演练场,一步步拆解从原始数据到深度见解的全流程。

1. 数据基石:理解、获取与初步清洗

在挥舞数据分析的“魔法棒”之前,我们必须先了解手中的“魔杖”是什么。县域经济数据通常以面板数据(Panel Data)的形式存在,即同一组县域(截面单元)在多个时间点(时间序列)上的观测值。这带来了巨大的分析优势,也伴随着数据整理的挑战。

1.1 核心字段解读与数据加载

一份典型的县域统计年鉴数据集可能包含数十个字段,我们可以将其大致归为几类:

  • 标识信息:年份、省份、县名、行政区划代码。这是数据连接的骨架,尤其是行政区划代码,它是唯一且稳定的标识(尽管历史上可能有调整)。
  • 人口与劳动力:户籍人口、乡村人口、乡村从业人员、二三产业从业人员等。反映地区的人力资源基本面。
  • 宏观经济产出:地区生产总值(GDP)、第一、二、三产业增加值。这是衡量经济规模与结构的核心。
  • 财政与金融:一般公共预算收入与支出、各项税收、居民储蓄存款、金融机构贷款余额。体现政府财力与金融活跃度。
  • 产业发展细节:工业增加值、规模以上工业企业数及总产值、农业机械总动力、粮食及经济作物产量、肉类产量等。用于分析产业内部结构。
  • 社会与公共服务:中小学在校学生数、医疗卫生机构床位数、社会福利单位数等。衡量社会发展与民生投入。

拿到数据(通常是CSV或Excel格式)后,第一步是用pandas将其载入内存,并进行初步审视。

import pandas as pd
import numpy as np

# 假设数据文件为 'county_data.csv'
df = pd.read_csv('county_data.csv', encoding='utf-8', low_memory=False)

# 首次见面:了解数据全貌
print(f"数据集形状: {df.shape}")  # (行数,列数)
print("\n前5行数据:")
print(df.head())
print("\n数据基本信息:")
print(df.info())
print("\n描述性统计(数值型字段):")
print(df.describe())

df.info()会告诉我们每一列的非空值数量和数据类型,这是发现数据质量问题的第一步。我们很可能会发现,许多数值型字段被识别为object类型,这通常是因为数据中存在缺失值(如空字符串、NA)或千分位分隔符(如1,234.5)。

1.2 数据清洗的“重头戏”:处理缺失值与异常值

真实数据很少是完美的。清洗是保证后续分析可靠性的关键。

处理缺失值:首先需要区分缺失的原因。是“确实没有发生”(如某县当年无规模以上工业企业),还是“数据未采集”?策略需结合业务判断。

# 检查缺失值比例
missing_ratio = df.isnull().sum() / len(df) * 100
missing_ratio_sorted = missing_ratio.sort_values(ascending=False)
print("各字段缺失值比例(降序):")
print(missing_ratio_sorted.head(20)) # 查看缺失最严重的20个字段

# 策略1:对于关键指标(如GDP),若缺失严重,考虑剔除该年份或该县的数据
# 策略2:对于部分缺失,可根据情况用中位数、均值(需谨慎,避免引入偏差)或前后年份插值填充
# 例如,对‘地区生产总值’按县进行时间序列线性插值
df['地区生产总值(万元)'] = df.groupby('县区市')['地区生产总值(万元)'].transform(
    lambda x: x.interpolate(method='linear')
)
# 策略3:对于分类变量或文本标识字段,用‘未知’等填充
df['省份'].fillna('未知', inplace=True)

处理异常值:异常值可能是录入错误,也可能是真实存在的极端情况(如资源型城市或特殊功能区)。常用方法是基于统计学(如3σ原则)或分位数进行识别。

# 以‘人均GDP’(需计算)为例,识别极端高值和低值
df['人均GDP'] = df['地区生产总值(万元)'] * 10000 / df['户籍人口(万人)']  # 转换为元/人
Q1 = df['人均GDP'].quantile(0.25)
Q3 = df['人均GDP'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

outliers = df[(df['人均GDP'] < lower_bound) | (df['人均GDP'] > upper_bound)]
print(f"基于IQR方法识别出人均GDP异常值记录数: {len(outliers)}")
# 对于异常值,可以标记、进一步审查或使用缩尾处理(Winsorization)
from scipy.stats.mstats import winsorize
df['人均GDP_winsorized'] = winsorize(df['人均GDP'].dropna(), limits=[0.05, 0.05])

统一单位与格式:确保所有数值字段的单位一致(如GDP统一为“万元”),并处理可能存在的文本格式数字。

# 示例:清理带逗号的数字字符串,如“1,234.50”
def clean_numeric_string(val):
    if isinstance(val, str):
        # 移除逗号、空格等非数字字符(除小数点外)
        val = val.replace(',', '').replace(' ', '')
        try:
            return float(val)
        except ValueError:
            return np.nan
    return val

numeric_columns = ['地区生产总值(万元)', '一般公共预算收入(万元)', ...] # 列出所有数值列
for col in numeric_columns:
    if df[col].dtype == 'object':
        df[col] = df[col].apply(clean_numeric_string)

2. 核心分析:从宏观趋势到微观洞察

数据清洗完毕后,我们便进入了分析的“主菜”环节。我们可以从多个维度切入,构建起对县域经济动态的立体认知。

2.1 宏观趋势分析:全国与区域层面的演变

首先,从整体上把握二十年间的主要变化。我们可以计算全国层面各项经济指标的年度总量或均值,观察其增长轨迹。

import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn-v0_8-whitegrid') # 设置绘图风格
sns.set_palette("husl")

# 计算全国年度GDP总量
national_gdp = df.groupby('年份')['地区生产总值(万元)'].sum() / 1e8  # 转换为亿元
national_pop = df.groupby('年份')['户籍人口(万人)'].sum()

# 计算全国人均GDP
national_gdp_per_capita = (national_gdp * 1e4) / (national_pop * 1e4) # 元/人

fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 子图1:GDP总量趋势
axes[0].plot(national_gdp.index, national_gdp.values, marker='o', linewidth=2)
axes[0].set_title('全国县域GDP总量变化趋势 (1997-2018)', fontsize=14)
axes[0].set_xlabel('年份')
axes[0].set_ylabel('GDP总量 (亿元)')
axes[0].grid(True, linestyle='--', alpha=0.7)

# 子图2:人均GDP趋势
axes[1].plot(national_gdp_per_capita.index, national_gdp_per_capita.values, marker='s', color='orange', linewidth=2)
axes[1].set_title('全国县域人均GDP变化趋势 (1997-2018)', fontsize=14)
axes[1].set_xlabel('年份')
axes[1].set_ylabel('人均GDP (元)')
axes[1].grid(True, linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

更进一步,我们可以分区域(如东、中、西部)进行对比分析,揭示发展的不平衡性。这需要先根据省份信息对县域进行区域划分。

2.2 产业结构变迁:从“二三一”到“三二一”?

产业结构升级是经济发展的重要标志。我们可以计算每个县每年第一、二、三产业增加值占GDP的比重,并分析其变化。

# 计算产业结构
df['第一产业占比'] = df['第一产业增加值(万元)'] / df['地区生产总值(万元)']
df['第二产业占比'] = df['第二产业增加值(万元)'] / df['地区生产总值(万元)']
df['第三产业占比'] = df['第三产业增加值(万元)'] / df['地区生产总值(万元)']

# 选取一个代表性省份(如浙江省)观察其县域平均产业结构变化
province = '浙江省'
province_df = df[df['省份'] == province]
industry_share_province = province_df.groupby('年份')[['第一产业占比', '第二产业占比', '第三产业占比']].mean()

# 绘制堆叠面积图
fig, ax = plt.subplots(figsize=(10, 6))
ax.stackplot(industry_share_province.index,
             industry_share_province['第一产业占比'],
             industry_share_province['第二产业占比'],
             industry_share_province['第三产业占比'],
             labels=['第一产业', '第二产业', '第三产业'],
             colors=['#4daf4a', '#377eb8', '#e41a1c'], alpha=0.8)
ax.set_title(f'{province}县域平均产业结构演变 (1997-2018)', fontsize=15)
ax.set_xlabel('年份')
ax.set_ylabel('产业占比')
ax.legend(loc='upper left')
ax.set_ylim(0, 1)
plt.show()

为了更细致地观察,我们可以计算每个县产业占比的年度变化,并找出那些产业结构发生显著转型的县域(例如,第三产业占比在十年内提升超过20个百分点)。

2.3 财政与民生关联分析:钱花在哪里,效果如何?

一般公共预算支出是政府提供公共服务能力的重要体现。我们可以分析财政支出结构(如计算人均教育支出、人均卫生支出)与一些社会发展指标(如中学在校生比例、每千人床位数)之间的相关性。

# 计算人均公共预算支出和人均民生相关支出(示例)
df['人均公共预算支出(元)'] = df['一般公共预算支出(万元)'] * 10000 / (df['户籍人口(万人)'] * 10000)
# 假设‘普通中学在校学生数(人)’和‘小学在校学生数(人)’字段存在
df['中小学在校生占比'] = (df['普通中学在校学生数(人)'] + df['小学在校学生数(人)']) / (df['户籍人口(万人)'] * 10000)
df['每千人医疗床位数'] = df['医疗卫生机构床位数(床)'] / (df['户籍人口(万人)'] * 10)

# 选取某一年份(如2018年)进行截面数据分析
df_2018 = df[df['年份'] == 2018].dropna(subset=['人均公共预算支出(元)', '中小学在校生占比', '每千人医疗床位数'])

# 绘制散点图与拟合线
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 子图1:人均支出 vs 在校生占比
sns.regplot(ax=axes[0], data=df_2018, x='人均公共预算支出(元)', y='中小学在校生占比',
            scatter_kws={'s':10, 'alpha':0.5}, line_kws={'color':'red'})
axes[0].set_title('人均财政支出与中小学在校生占比 (2018)')
axes[0].set_xlabel('人均公共预算支出 (元)')
axes[0].set_ylabel('中小学在校生占比')

# 子图2:人均支出 vs 每千人床位数
sns.regplot(ax=axes[1], data=df_2018, x='人均公共预算支出(元)', y='每千人医疗床位数',
            scatter_kws={'s':10, 'alpha':0.5}, line_kws={'color':'red'})
axes[1].set_title('人均财政支出与每千人医疗床位数 (2018)')
axes[1].set_xlabel('人均公共预算支出 (元)')
axes[1].set_ylabel('每千人医疗床位数')

plt.tight_layout()
plt.show()

# 计算相关系数
corr_student = df_2018['人均公共预算支出(元)'].corr(df_2018['中小学在校生占比'])
corr_bed = df_2018['人均公共预算支出(元)'].corr(df_2018['每千人医疗床位数'])
print(f"人均支出与在校生占比的相关系数: {corr_student:.3f}")
print(f"人均支出与每千人床位数的相关系数: {corr_bed:.3f}")

注意:相关性不等于因果性。更高的财政支出可能源于更强的经济基础,而更强的经济基础本身就能支撑更好的教育和医疗。更严谨的分析需要控制其他变量,或使用面板数据模型。

3. 空间可视化:让数据在地图上“说话”

经济现象具有强烈的空间属性。将分析结果映射到地理空间上,能直观揭示集群效应、梯度差异和空间相关性。这里我们需要地理信息数据(如县域行政区划的GeoJSON或Shapefile文件)和geopandas库。

3.1 基础地图绘制:人均GDP的空间分布

假设我们已有一个与统计数据集行政区划代码匹配的GeoDataFrame gdf

import geopandas as gpd

# 加载地理数据
# gdf = gpd.read_file('china_counties.geojson') # 示例
# 假设gdf中有一个字段‘code’与df中的‘地区编码(最新)’匹配

# 准备2018年的经济数据
df_2018_econ = df[df['年份'] == 2018][['地区编码(最新)', '人均GDP', '县区市']]
# 合并地理数据与经济数据
merged_gdf = gdf.merge(df_2018_econ, left_on='code', right_on='地区编码(最新)', how='left')

# 绘制分级色彩地图
fig, ax = plt.subplots(1, 1, figsize=(12, 10))
# 对人均GDP进行分位数分级,以更好地显示分布
merged_gdf['人均GDP_qcut'] = pd.qcut(merged_gdf['人均GDP'], q=5, labels=False, duplicates='drop')
merged_gdf.plot(column='人均GDP_qcut', ax=ax, legend=True,
                legend_kwds={'label': "人均GDP分位数等级 (1低-5高)", 'orientation': "horizontal"},
                cmap='OrRd', edgecolor='black', linewidth=0.2)
ax.set_title('2018年中国县域人均GDP空间分布(五分位图)', fontsize=16)
ax.axis('off') # 关闭坐标轴
plt.show()

3.2 动态演进地图:展示变化过程

要展示多年变化,可以制作一系列年份的地图,组合成动画或并排对比。这里展示并排对比的方法。

# 选取几个关键年份,如2000, 2008, 2018
years_to_plot = [2000, 2008, 2018]
fig, axes = plt.subplots(1, len(years_to_plot), figsize=(18, 6))

for idx, year in enumerate(years_to_plot):
    df_year = df[df['年份'] == year][['地区编码(最新)', '人均GDP']]
    merged_gdf_year = gdf.merge(df_year, left_on='code', right_on='地区编码(最新)', how='left')
    # 使用统一的颜色分级标准(基于所有年份的数据范围),便于比较
    vmin, vmax = df['人均GDP'].quantile(0.05), df['人均GDP'].quantile(0.95)
    merged_gdf_year.plot(column='人均GDP', ax=axes[idx], cmap='YlGnBu',
                         legend=(idx == len(years_to_plot)-1), # 只在最后一个子图显示图例
                         legend_kwds={'shrink': 0.7, 'label': '人均GDP (元)'},
                         vmin=vmin, vmax=vmax,
                         edgecolor='grey', linewidth=0.1)
    axes[idx].set_title(f'{year}年', fontsize=14)
    axes[idx].axis('off')

plt.suptitle('县域人均GDP空间分布演变', fontsize=18, y=0.95)
plt.tight_layout()
plt.show()

4. 高级分析与建模探索

在描述性分析的基础上,我们可以尝试一些更深入的探索,例如识别相似县域集群、构建经济发展预测指标等。

4.1 县域经济类型聚类分析

使用无监督的聚类算法(如K-Means),根据多个经济指标(如人均GDP、产业结构、财政自给率、人口密度等)对县域进行分类。

from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import warnings
warnings.filterwarnings('ignore')

# 准备2018年数据,选择特征
features = ['人均GDP', '第二产业占比', '第三产业占比', '人均公共预算支出(元)', '每千人医疗床位数']
df_2018_for_cluster = df_2018[['县区市'] + features].dropna()

X = df_2018_for_cluster[features].values
# 标准化特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 使用肘部法则初步确定K值(这里简化为选择K=4)
# 实际分析中应运行循环计算不同K值的 inertia_
kmeans = KMeans(n_clusters=4, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X_scaled)
df_2018_for_cluster['Cluster'] = clusters

# 使用PCA降维到2维进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=clusters, cmap='tab10', s=30, alpha=0.7)
plt.xlabel('主成分 1')
plt.ylabel('主成分 2')
plt.title('县域经济特征聚类结果 (PCA降维可视化)')
plt.colorbar(scatter, label='聚类标签')
plt.show()

# 查看每个簇的特征均值
cluster_profile = df_2018_for_cluster.groupby('Cluster')[features].mean()
print(cluster_profile)

我们可以根据cluster_profile的结果,为每个簇赋予业务含义,例如“高收入服务主导型”、“工业驱动增长型”、“农业为主待发展型”、“均衡中等收入型”。

4.2 面板数据模型初探

对于有时间维度的面板数据,我们可以建立计量经济模型来探究变量间的因果关系。一个简单的起点是构建一个县域经济增长(如人均GDP增长率)的影响因素模型。

import statsmodels.api as sm
import statsmodels.formula.api as smf

# 准备面板数据格式:需要确保每个县-年对有一条记录
# 计算人均GDP增长率
df['log_gdp_pc'] = np.log(df['人均GDP']) # 取对数
df = df.sort_values(['地区编码(最新)', '年份'])
df['growth_gdp_pc'] = df.groupby('地区编码(最新)')['log_gdp_pc'].diff() # 计算对数差分,即近似增长率

# 选择解释变量,并处理缺失值
model_df = df[['地区编码(最新)', '年份', 'growth_gdp_pc',
               '第二产业占比', '固定资产投资(万元)', '人均公共预算支出(元)',
               '中小学在校生占比']].dropna()
# 为简化,这里使用混合OLS模型。更严谨的做法应使用固定效应或随机效应模型。
model = smf.ols('growth_gdp_pc ~ 第二产业占比 + np.log(固定资产投资(万元)) + 人均公共预算支出(元) + 中小学在校生占比',
                data=model_df).fit()
print(model.summary())

提示:面板数据分析涉及更复杂的模型设定(如固定效应、随机效应、工具变量法等)和严格的假设检验。上述OLS回归仅为示意,在实际研究中需根据理论、数据特征和检验结果选择合适的模型。

5. 构建可复用的分析管道与报告生成

一次性的分析脚本价值有限。我们可以将上述步骤模块化,封装成函数或类,形成一个可复用的分析管道。同时,利用Jupyter NotebookPython的报表生成库(如Jinja2 + WeasyPrint),可以将关键发现自动生成分析报告。

例如,我们可以创建一个CountyDataAnalyzer类,初始化时载入数据和地理信息,然后提供诸如plot_regional_trend()calculate_industry_structure()generate_county_profile(county_name)等方法。对于报告生成,可以定义一个模板,将核心图表和统计表格插入其中,一键输出PDF或HTML。

最后,我想分享一点个人在处理类似数据时的体会:县域数据的价值在于其“颗粒度”。它既能汇总反映宏观趋势,又能揭示被省级数据平均所掩盖的内部差异。分析时,一定要结合具体的地理背景和发展阶段来解读数字。比如,一个山区县的人均GDP增长,可能源于特色农业或旅游业,而非工业投资。多问一个“为什么”,数据背后的故事才会真正浮现。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐