数据格式与预处理:NetCDF、CSV、TXT 等常见格式解析,时间戳对齐,缺失值标记

各位同学,欢迎来到数据融合实战的第四讲。

说实话,做风资源评估这么多年,我见过最多的坑,不是算法不够高级,而是数据本身就没整明白。你想想看,LiDAR 扫出来的数据是 NetCDF,测风塔记的是 CSV,气象局给的是 TXT——三个文件三种脾气,时间戳还对不上。这要是直接扔进模型,结果能靠谱才怪。

所以这一章,咱们就专门来收拾这些「数据烂摊子」。我会带着你,把常见的格式挨个捋一遍,再教你怎么把时间戳对齐,最后把缺失值标记清楚。这些都是我当年踩过的坑,你学会了,能省下至少一半的调试时间。

4.1 三种常见格式的「脾气」与解析方法

先说说这三种格式各自的特点。我个人习惯把它们比作三种不同的工具箱:

  • NetCDF(.nc):科学计算界的「瑞士军刀」。自带维度、变量、属性,结构严谨。LiDAR 厂商最喜欢用这个格式,因为能塞进去一大堆元数据——经纬度、海拔、时间、风速廓线全在一起。但缺点也很明显:普通人不装库根本打不开。
  • CSV(.csv):最通用的「表格本」。测风塔数据十有八九是 CSV。简单、直观,Excel 就能看。但问题在于——各家测风塔的 CSV 格式五花八门,有的带表头,有的不带,有的时间列是字符串,有的是时间戳数字。
  • TXT(.txt):最原始的「记事本」。气象局或某些老旧设备还在用。格式自由度极高,但也最乱。我遇到过一行数据用空格、制表符、逗号混着分隔的,那叫一个酸爽。

核心原则:不管原始格式是什么,最终都要统一成 Pandas DataFrame。这是后续所有操作的基础。

4.1.1 解析 NetCDF(LiDAR 数据)

解析 NetCDF 需要 xarray 库。我个人强烈建议你装这个,比 netCDF4 底层库好用太多了。

import xarray as xr
import pandas as pd

# 读取 LiDAR 的 NetCDF 文件
ds = xr.open_dataset('lidar_data.nc')
print(ds)

# 提取关键变量:时间、风速、风向
time = ds['time'].values
wind_speed = ds['wind_speed'].values  # 假设变量名
wind_dir = ds['wind_direction'].values

# 转成 DataFrame
df_lidar = pd.DataFrame({
    'time': time,
    'ws_lidar': wind_speed,
    'wd_lidar': wind_dir
})

避坑指南:我曾经遇到过一个 LiDAR 数据,它的时间维度是「自 1970-01-01 以来的秒数」,但单位写的是「hours」。你解析时一定要检查 ds['time'].units 属性,否则时间会偏到姥姥家去。

4.1.2 解析 CSV(测风塔数据)

CSV 解析用 pandas.read_csv() 就够了。但要注意几个参数:

import pandas as pd

# 标准情况
df_tower = pd.read_csv('tower_data.csv', parse_dates=['time'])

# 如果时间列是分开的(年、月、日、时、分)
df_tower = pd.read_csv('tower_data.csv')
df_tower['time'] = pd.to_datetime(df_tower[['year','month','day','hour','minute']])

# 如果分隔符不是逗号(比如制表符)
df_tower = pd.read_csv('tower_data.txt', sep='\t')

注意:测风塔 CSV 经常有「表头注释行」,比如文件前几行是站点信息。这时候要用 skiprows=3 跳过。我见过最夸张的一个文件,前面有 20 行注释,不跳过的话数据全乱套。

4.1.3 解析 TXT(气象局或老旧设备)

TXT 是最头疼的。我的经验是:先打开看一眼,再写解析代码。

# 先看前10行,了解格式
with open('weather_data.txt', 'r') as f:
    for i in range(10):
        print(f.readline())

# 假设格式:时间戳 风速 风向(空格分隔)
df_weather = pd.read_csv('weather_data.txt', sep='\s+', 
                         names=['time', 'ws', 'wd'],
                         parse_dates=['time'])

我的经验:TXT 文件最容易出现「空行」和「乱码」。建议解析后立刻检查 df.info()df.isnull().sum(),把明显异常的行先标记出来。

4.2 时间戳对齐——数据融合的「卡脖子」环节

好,现在三个文件都读进来了。但 LiDAR 是每 10 分钟一个数据点,测风塔是每 1 分钟一个,气象局是每小时一个。时间戳对不上,怎么融合?

说白了,时间戳对齐就两个思路:

  1. 降采样:把高频数据(1分钟)降成低频(10分钟),取平均或中位数。
  2. 重采样:把所有数据统一到同一个时间网格上,比如整点 00、10、20……

我个人更推荐第二种,因为能保留原始数据的统计特征。

# 假设 df_lidar 和 df_tower 都有 'time' 列
# 先把 time 列设为索引
df_lidar = df_lidar.set_index('time')
df_tower = df_tower.set_index('time')

# 重采样到 10 分钟间隔(整10分)
df_lidar_10min = df_lidar.resample('10T').mean()
df_tower_10min = df_tower.resample('10T').mean()

# 然后按时间索引对齐合并
df_merged = pd.merge(df_lidar_10min, df_tower_10min, 
                     left_index=True, right_index=True, how='outer')

避坑指南:我曾经犯过一个错——直接用 pd.merge(how='inner'),结果发现 LiDAR 和测风塔的时间戳差了 1 秒,导致合并后数据少了一半。后来我学乖了:先做 round 到最近分钟,再合并。

# 先四舍五入到分钟,避免微秒级偏差
df_lidar.index = df_lidar.index.round('1min')
df_tower.index = df_tower.index.round('1min')

4.3 缺失值标记——别急着删,先搞清楚为什么缺失

数据对齐之后,你会发现很多时间点只有 LiDAR 数据,没有测风塔数据,或者反过来。这就是缺失值。

我的原则是:先标记,后处理。不要一上来就 dropna(),那样会丢失大量信息。

4.3.1 标记缺失类型

我习惯在合并后的 DataFrame 里加一列,标记缺失的原因:

# 标记缺失类型
df_merged['missing_flag'] = '正常'

# LiDAR 缺失,测风塔正常
df_merged.loc[df_merged['ws_lidar'].isna() & df_merged['ws_tower'].notna(), 'missing_flag'] = '仅测风塔'

# 测风塔缺失,LiDAR 正常
df_merged.loc[df_merged['ws_lidar'].notna() & df_merged['ws_tower'].isna(), 'missing_flag'] = '仅LiDAR'

# 两者都缺失
df_merged.loc[df_merged['ws_lidar'].isna() & df_merged['ws_tower'].isna(), 'missing_flag'] = '全部缺失'

4.3.2 统计缺失情况

标记完之后,做个统计,心里就有数了:

# 统计各类缺失的数量和占比
missing_stats = df_merged['missing_flag'].value_counts()
missing_ratio = df_merged['missing_flag'].value_counts(normalize=True) * 100

print("缺失类型统计:")
print(missing_stats)
print("\n缺失占比(%):")
print(missing_ratio)

我的经验:如果「仅LiDAR」缺失占比超过 20%,说明测风塔在那个时段可能停机了。如果「仅测风塔」缺失多,可能是 LiDAR 的探测高度和测风塔不匹配。这些信息对后续融合策略的选择至关重要。

4.4 本章知识体系总览

下面这张图,是我自己梳理的数据预处理流程。你照着这个走,基本不会出大问题。

数据格式与预处理核心流程 NetCDF (LiDAR) xarray.open_dataset() CSV (测风塔) pd.read_csv() TXT (气象局) pd.read_csv(sep='\s+') 统一为 Pandas DataFrame 时间戳对齐:resample() + round() 缺失值标记:分类 + 统计 融合就绪的干净数据集

4.5 本章小结

嗯,这一章的内容其实就三件事:

  • 读进来:NetCDF 用 xarray,CSV 用 pandas,TXT 先看再读。
  • 对齐时间:重采样到统一网格,别忘了 round 一下避免微秒偏差。
  • 标记缺失:分清楚是「谁缺失」以及「为什么缺失」,别一股脑删掉。

你把这些基础打牢了,后面做融合算法的时候,心里才有底。数据预处理虽然枯燥,但它是整个分析流程的基石。我见过太多人急着跑模型,结果被数据格式问题卡了三天——何必呢?

一个小建议:每次拿到新数据,先花 10 分钟做一次完整的格式检查和缺失统计。这 10 分钟,能帮你省下后面 10 小时的 debug 时间。我自己就是这么干的,屡试不爽。

专注资料整理