data-sci-project/buildings_and_incomes_by_postal_code
Exploratory Data Analysis - Paavo Dataset An exploratory data analysis (EDA) pipeline for Statistics Finland's Paavo open dataset, which provides area and municipality-level statistics on demographics, income levels, household structures, and building infrastructure across Finland. Project Overview This script processes the Paavo dataset to generate summary statistics, distribution plots, correlation analysis, and targeted visualizations for key metrics across… See the full description on the dataset page: https://huggingface.co/datasets/data-sci-project/buildings_and_incomes_by_postal_code.
070
1import pandas as pd
2import matplotlib.pyplot as plt
3import seaborn as sns
4
5OUT = "eda_outputs"
6import os
7os.makedirs(OUT, exist_ok=True)
8
9df = pd.read_csv("PaavoDataset.csv", encoding="latin1", sep=";")
10df.columns = df.columns.str.strip() # remove stray leading/trailing spaces
11df = df[~df["Area"].str.strip().str.startswith("MK")].copy() # Drop regional summary rows (e.g. "MK01 Uusimaa")
12
13#Summary stats
14print("--- Info ---")
15print(df.info())
16print("\n--- Descriptive statistics ---")
17desc = df.describe().T
18print(desc)
19desc.to_csv(f"{OUT}/summary_statistics.csv")
20
21# Distributions of key variables
22key_vars = [
23 "Inhabitants",
24 "Average age of inhabitants",
25 "Average income of inhabitants",
26 "Median income of inhabitants",
27 "Average size of households",
28]
29
30fig, axes = plt.subplots(len(key_vars), 1, figsize=(8, 4 * len(key_vars)))
31for ax, col in zip(axes, key_vars):
32 sns.histplot(df[col], kde=True, ax=ax, color="#6fa8dc")
33 ax.set_title(f"Distribution of {col}")
34plt.tight_layout()
35fig.savefig(f"{OUT}/01_distributions.png", dpi=150)
36plt.close(fig)
37
38# Correlation heatmap (numeric columns only)
39numeric_df = df.select_dtypes(include="number")
40corr = numeric_df.corr()
41
42fig, ax = plt.subplots(figsize=(14, 12))
43sns.heatmap(corr, cmap="coolwarm", center=0, annot=False, ax=ax)
44ax.set_title("Correlation heatmap")
45fig.savefig(f"{OUT}/02_correlation_heatmap.png", dpi=150, bbox_inches="tight")
46plt.close(fig)
47
48# Top areas by income and population
49top_income = df.nlargest(15, "Average income of inhabitants")[["Area", "Average income of inhabitants"]]
50top_population = df.nlargest(15, "Inhabitants")[["Area", "Inhabitants"]]
51print("\n--- Top 15 areas by average income ---")
52print(top_income)
53print("\n--- Top 15 areas by population ---")
54print(top_population)
55
56# Income category composition (stacked bar, top 20 by inhabitants)
57income_cats = [
58 "Inhabitants belonging to the lowest income category",
59 "Inhabitants belonging to the middle income category",
60 "Inhabitants belonging to the highest income category",
61]
62
63top20_income = df.nlargest(20, "Inhabitants").set_index("Area")[income_cats].iloc[::-1]
64top20_income.columns = ["Lowest income cat.", "Middle income cat.", "Highest income cat."]
65
66fig, ax = plt.subplots(figsize=(10, 8))
67top20_income.plot(kind="barh", stacked=True, color=["#6fa8dc", "#cc6659", "#e69138"], ax=ax)
68ax.set_title("Income category composition - top 20 areas by population")
69ax.set_xlabel("Number of inhabitants")
70fig.savefig(f"{OUT}/03_income_categories_top20.png", dpi=150, bbox_inches="tight")
71plt.close(fig)
72
73# Household income category composition (same style, top 20 by households)
74household_cats = [
75 "Households belonging to the lowest income category",
76 "Households belonging to the middle income category",
77 "Households belonging to the highest income category",
78]
79
80top20_households = (df.nlargest(20, "Households").set_index("Area")[household_cats].iloc[::-1])
81top20_households.columns = ["Lowest income cat.", "Middle income cat.", "Highest income cat."]
82
83fig, ax = plt.subplots(figsize=(10, 8))
84top20_households.plot(kind="barh", stacked=True, color=["#6fa8dc", "#cc6659", "#e69138"], ax=ax)
85ax.set_title("Household income category composition - top 20 areas by number of households")
86ax.set_xlabel("Number of households")
87fig.savefig(f"{OUT}/04_household_income_categories_top20.png", dpi=150, bbox_inches="tight")
88plt.close(fig)
89
90# Inhabitants per residential building (housing density proxy)
91df["Inhabitants per residential building"] = (
92 df["Inhabitants"] / df["Residential buildings"].replace(0, pd.NA)
93)
94
95density_top20 = df.nlargest(20, "Inhabitants per residential building")[
96 ["Area", "Inhabitants per residential building"]
97].set_index("Area")
98
99fig, ax = plt.subplots(figsize=(9, 8))
100density_top20.sort_values("Inhabitants per residential building").plot(
101 kind="barh", legend=False, color="#6fa8dc", ax=ax
102)
103ax.set_title("Top 20 areas by inhabitants per residential building)")
104ax.set_xlabel("Inhabitants per residential building")
105fig.savefig(f"{OUT}/05_inhabitants_per_building_top20.png", dpi=150, bbox_inches="tight")
106plt.close(fig)
107
108# Accumulated purchasing power of inhabitants
109purchasing_top20 = df.nlargest(20, "Accumulated purchasing power of inhabitants")[
110 ["Area", "Accumulated purchasing power of inhabitants"]
111].set_index("Area")
112
113fig, ax = plt.subplots(figsize=(9, 8))
114purchasing_top20.sort_values("Accumulated purchasing power of inhabitants").plot(
115 kind="barh", legend=False, color="#e69138", ax=ax
116)
117ax.set_title("Top 20 areas by accumulated purchasing power of inhabitants")
118ax.set_xlabel("Accumulated purchasing power (EUR)")
119fig.savefig(f"{OUT}/06_purchasing_power_top20.png", dpi=150, bbox_inches="tight")
120plt.close(fig)
121
122print(f"\nAll plots and summary CSV saved to ./{OUT}/")