← 返回 Tessera
气象600 行 × 622 KB无缺失数据 2026-04-12

weather_cities

一个数值列、两级分类、一根时间轴,还没有缺失——试折线、热图、分面和发散配色的首选靶子。

Open-Meteo Archive API · 收录于 2026-04-12

下载 CSV · 22 KB
数据预览6
citycountrycontinentyearmonthavg_temp_c
TokyoJapanAsia2,02314
TokyoJapanAsia2,02325.7
TokyoJapanAsia2,023312.0
TokyoJapanAsia2,023415.2
TokyoJapanAsia2,023518.0
TokyoJapanAsia2,023622.5
变量6
字符型3整数2双精度1
类型
#变量类型缺失统计
1city字符型不同值 50Tokyo · Beijing · Shanghai · Hong Kong · Singapore · Bangkok · Mumbai · Delhi
2country字符型不同值 40USA · China · Australia · India · Canada · Japan · Singapore · Thailand
3continent字符型不同值 7Asia · Europe · North America · Africa · South America · Middle East · Oceania
4year整数min 2,023q1 中位 2,023均值 2,023q3 max 2,023
5month由 daily.time 取月,timezone=auto整数min 1q1 中位 6.5均值 6.5q3 max 12.0
6avg_temp_ckey= mean(daily.temperature_2m_mean) by month双精度min -6.1q1 中位 18.4均值 17.8q3 max 37.6
载入已写好列类型
library(readr)

weather_cities <- read_csv(
  "https://assets.evanzhou.org/tessera/csv/weather_cities.csv",
  col_types = cols(
    city       = col_character(),
    country    = col_character(),
    continent  = col_character(),
    year       = col_integer(),
    month      = col_integer(),
    avg_temp_c = col_double()
  )
)
URLhttps://assets.evanzhou.org/tessera/csv/weather_cities.csv

来源

50 座城市 2023 年的逐月平均气温,取自 Open-Meteo 的历史归档 API。一行是一个城市的一个月。

城市名单是手写的,按大洲配额挑的:亚洲 12、欧洲 11、北美 8、非洲 6、南美 5、中东 4、大洋洲 4。所以大洲之间样本量不均等——按大洲聚合时记得这不是均衡设计。

适用图形

图形结构 用途
折线图 单个城市的逐月气温曲线
热图 城市 × 月份 的气温矩阵
分面小图 按大洲分组看季节形态
排序点图 城市的年度或季节汇总排名

气温天然有两侧、中间锚在 0 ℃,是试发散型配色现成的靶子。南北半球的季节相反,画折线时把两个半球分开或者用颜色区分,否则曲线会拧成一团。

生成脚本Python · 126
"""
weather_cities.py
Fetch monthly mean temperature for 50 cities (year 2023) via Open-Meteo.
Output: assets/toy/weather/weather_cities.csv (600 rows)
"""

from pathlib import Path
import sys
import time
import requests
import pandas as pd

sys.stdout.reconfigure(encoding="utf-8")

YEAR = 2023

CITIES = [
    # Asia (12)
    {"city": "Tokyo",        "country": "Japan",        "continent": "Asia",          "lat": 35.68,  "lon": 139.69},
    {"city": "Beijing",      "country": "China",        "continent": "Asia",          "lat": 39.90,  "lon": 116.41},
    {"city": "Shanghai",     "country": "China",        "continent": "Asia",          "lat": 31.23,  "lon": 121.47},
    {"city": "Hong Kong",    "country": "China",        "continent": "Asia",          "lat": 22.32,  "lon": 114.17},
    {"city": "Singapore",    "country": "Singapore",    "continent": "Asia",          "lat":  1.35,  "lon": 103.82},
    {"city": "Bangkok",      "country": "Thailand",     "continent": "Asia",          "lat": 13.76,  "lon": 100.50},
    {"city": "Mumbai",       "country": "India",        "continent": "Asia",          "lat": 19.08,  "lon":  72.88},
    {"city": "Delhi",        "country": "India",        "continent": "Asia",          "lat": 28.61,  "lon":  77.21},
    {"city": "Seoul",        "country": "South Korea",  "continent": "Asia",          "lat": 37.57,  "lon": 126.98},
    {"city": "Kuala Lumpur", "country": "Malaysia",     "continent": "Asia",          "lat":  3.14,  "lon": 101.69},
    {"city": "Jakarta",      "country": "Indonesia",    "continent": "Asia",          "lat": -6.21,  "lon": 106.85},
    {"city": "Karachi",      "country": "Pakistan",     "continent": "Asia",          "lat": 24.86,  "lon":  67.01},
    # Europe (11)
    {"city": "London",       "country": "UK",           "continent": "Europe",        "lat": 51.51,  "lon":  -0.13},
    {"city": "Paris",        "country": "France",       "continent": "Europe",        "lat": 48.86,  "lon":   2.35},
    {"city": "Berlin",       "country": "Germany",      "continent": "Europe",        "lat": 52.52,  "lon":  13.41},
    {"city": "Moscow",       "country": "Russia",       "continent": "Europe",        "lat": 55.76,  "lon":  37.62},
    {"city": "Madrid",       "country": "Spain",        "continent": "Europe",        "lat": 40.42,  "lon":  -3.70},
    {"city": "Rome",         "country": "Italy",        "continent": "Europe",        "lat": 41.90,  "lon":  12.50},
    {"city": "Amsterdam",    "country": "Netherlands",  "continent": "Europe",        "lat": 52.37,  "lon":   4.90},
    {"city": "Stockholm",    "country": "Sweden",       "continent": "Europe",        "lat": 59.33,  "lon":  18.07},
    {"city": "Istanbul",     "country": "Turkey",       "continent": "Europe",        "lat": 41.01,  "lon":  28.98},
    {"city": "Zurich",       "country": "Switzerland",  "continent": "Europe",        "lat": 47.38,  "lon":   8.54},
    {"city": "Reykjavik",    "country": "Iceland",      "continent": "Europe",        "lat": 64.13,  "lon": -21.82},
    # North America (8)
    {"city": "New York",     "country": "USA",          "continent": "North America", "lat": 40.71,  "lon": -74.01},
    {"city": "Los Angeles",  "country": "USA",          "continent": "North America", "lat": 34.05,  "lon": -118.24},
    {"city": "Chicago",      "country": "USA",          "continent": "North America", "lat": 41.88,  "lon": -87.63},
    {"city": "Miami",        "country": "USA",          "continent": "North America", "lat": 25.76,  "lon": -80.19},
    {"city": "Houston",      "country": "USA",          "continent": "North America", "lat": 29.76,  "lon": -95.37},
    {"city": "Toronto",      "country": "Canada",       "continent": "North America", "lat": 43.65,  "lon": -79.38},
    {"city": "Vancouver",    "country": "Canada",       "continent": "North America", "lat": 49.28,  "lon": -123.12},
    {"city": "Mexico City",  "country": "Mexico",       "continent": "North America", "lat": 19.43,  "lon": -99.13},
    # South America (5)
    {"city": "São Paulo",    "country": "Brazil",       "continent": "South America", "lat": -23.55, "lon": -46.63},
    {"city": "Buenos Aires", "country": "Argentina",    "continent": "South America", "lat": -34.60, "lon": -58.38},
    {"city": "Lima",         "country": "Peru",         "continent": "South America", "lat": -12.05, "lon": -77.04},
    {"city": "Bogotá",       "country": "Colombia",     "continent": "South America", "lat":  4.71,  "lon": -74.07},
    {"city": "Santiago",     "country": "Chile",        "continent": "South America", "lat": -33.45, "lon": -70.67},
    # Africa (6)
    {"city": "Cairo",        "country": "Egypt",        "continent": "Africa",        "lat": 30.04,  "lon":  31.24},
    {"city": "Lagos",        "country": "Nigeria",      "continent": "Africa",        "lat":  6.52,  "lon":   3.38},
    {"city": "Nairobi",      "country": "Kenya",        "continent": "Africa",        "lat": -1.29,  "lon":  36.82},
    {"city": "Cape Town",    "country": "South Africa", "continent": "Africa",        "lat": -33.92, "lon":  18.42},
    {"city": "Casablanca",   "country": "Morocco",      "continent": "Africa",        "lat": 33.57,  "lon":  -7.59},
    {"city": "Addis Ababa",  "country": "Ethiopia",     "continent": "Africa",        "lat":  9.03,  "lon":  38.74},
    # Middle East (4)
    {"city": "Dubai",        "country": "UAE",          "continent": "Middle East",   "lat": 25.20,  "lon":  55.27},
    {"city": "Riyadh",       "country": "Saudi Arabia", "continent": "Middle East",   "lat": 24.71,  "lon":  46.68},
    {"city": "Tel Aviv",     "country": "Israel",       "continent": "Middle East",   "lat": 32.09,  "lon":  34.78},
    {"city": "Tehran",       "country": "Iran",         "continent": "Middle East",   "lat": 35.69,  "lon":  51.39},
    # Oceania (4)
    {"city": "Sydney",       "country": "Australia",    "continent": "Oceania",       "lat": -33.87, "lon": 151.21},
    {"city": "Melbourne",    "country": "Australia",    "continent": "Oceania",       "lat": -37.81, "lon": 144.96},
    {"city": "Brisbane",     "country": "Australia",    "continent": "Oceania",       "lat": -27.47, "lon": 153.03},
    {"city": "Auckland",     "country": "New Zealand",  "continent": "Oceania",       "lat": -36.85, "lon": 174.76},
]


def fetch_monthly(city: dict) -> pd.DataFrame:
    resp = requests.get(
        "https://archive-api.open-meteo.com/v1/archive",
        params={
            "latitude":   city["lat"],
            "longitude":  city["lon"],
            "start_date": f"{YEAR}-01-01",
            "end_date":   f"{YEAR}-12-31",
            "daily":      "temperature_2m_mean",
            "timezone":   "auto",
        },
        timeout=30,
    )
    resp.raise_for_status()
    daily = resp.json()["daily"]

    df = pd.DataFrame({"date": daily["time"], "temp_c": daily["temperature_2m_mean"]})
    df["date"] = pd.to_datetime(df["date"])
    df["month"] = df["date"].dt.month

    monthly = df.groupby("month")["temp_c"].mean().reset_index()
    monthly.rename(columns={"temp_c": "avg_temp_c"}, inplace=True)
    monthly["avg_temp_c"] = monthly["avg_temp_c"].round(1)
    monthly["city"]      = city["city"]
    monthly["country"]   = city["country"]
    monthly["continent"] = city["continent"]
    monthly["year"]      = YEAR

    return monthly[["city", "country", "continent", "year", "month", "avg_temp_c"]]


def main():
    records = []
    for i, city in enumerate(CITIES, 1):
        print(f"[{i:02d}/50] {city['city']}...")
        try:
            records.append(fetch_monthly(city))
        except Exception as e:
            print(f"  ERROR: {e}")
        time.sleep(0.3)

    result = pd.concat(records, ignore_index=True)
    out = Path(__file__).with_name("weather_cities.csv")
    result.to_csv(out, index=False)
    print(f"\nDone — {len(result)} rows saved to {out}")


if __name__ == "__main__":
    main()

表中统计由 scripts/profile_dataset.py 于 2026-08-03 数出。