SatyrLee
文章91
标签27
分类11

一言

文章归档

降水序列重建思路

降水序列重建思路

封面图 ID:101415437

时隔时间很长的一个更新,之前一直在忙于很多事情,接下来还有一些内容可供分享。

0x00 引子

前几天接到了一批不同时间长度的降雨序列数据,需要将其处理为逐小时降水序列数据。然而处理的时候发现了一些问题,时间序列长度不一致,且有冲突。通过结合历史资料分析,提出了一种不是很合理的解决思路,并将其编写为代码,整理如下。

先放上一部分样例数据:

Start Time End Time Value
2016-06-01 04:00 2016-06-01 08:00 2.9
2016-06-01 08:00 2016-06-01 16:00 3.5
2016-06-01 14:00 2016-06-01 16:00 0.5
2016-06-01 14:00 2016-06-01 15:00 0.8
2016-06-01 15:00 2016-06-01 16:00 10.9

0x01 问题的出现

其实最简单的思路就是直接将长时间的数据除以时间并进行拼接即可,但是在原数据中发现了问题:14:00-16:00 的降水量是 0.5 mm,而 14:00-15:00 的降水量是 0.8 mm,这在时间段降水量定义上明显存在冲突。

但是我们不能简单就将其视为是异常数据就处理掉,因为在多个时间段内数据均有这样的问题。这时我想到在这里可能是表示为折算至每小时的降水量。同样,也不能简单的做出定论。需要进行数据支持。还好,有一些网站提供了历史降水量数据,可以进行简单比对。

这种历史天气网不会给出降水量,但是会给出中雨、大雨等天气状况。通过对比天气状况,可以大致判断降水量的范围。根据中国气象局的定义:

24 小时内降雨量 ﹤ 0.1 毫米之间为微量降雨(零星小雨);
12 小时内降雨量 0.1 ~ 4.9 毫米或 24 小时内降雨量 0.1 ~ 9.9 毫米之间为小雨;
12 小时内降雨量 5.0 ~ 14.9 毫米或 24 小时内降雨量在 10 ~ 24.9 毫米之间为中雨;
12 小时内降雨量 15.0 ~ 29.9 毫米或 24 小时内降雨量在 25.0 ~ 49.9 毫米之间为大雨;
12 小时内降雨量 30.0 ~ 69.9 毫米或 24 小时内降雨量在 50.0 ~ 99.9 毫米之间为暴雨;
12 小时内降雨量 70.0 ~ 139.9 毫米或 24 小时内降雨量在 100.0 ~ 249.9 毫米之间为大暴雨;
12 小时内降雨量 ≥ 140.0 毫米或 24 小时内降雨量 ≥ 250.0 毫米之间为特大暴雨。
(标准来源为国家标准 GB/T 28592-2012)

对比天气状况可知,当时天气情况为小雨,如果这是折算后的每小时降水量则明显超过小雨的标准。而对比其他数据也出现这种问题,因此说明这里还是时段内总降水量

0x02 解决思路

考虑雨量计的工作原理,雨量计是通过测量降水的高度来计算降水量的。雨量计通常会有一个容器,当降水落入容器时,容器内的水位会随着降水量的增加而上升。雨量计会定期记录容器内的水位高度,并将其转换为降水量。

这里就会有一些误差,如果同时段内有数据冲突,应更倾向于采信较短时段的降水量数据,而较长时段的可信度较低,越长时间的降水量数据可信度越低。因此,采用如下计算逻辑:

  1. 优先使用较短时间段的降水量数据,如果不是一个小时的,将其平均;
  2. 冲突数据进入时进行检查,如果大于当前时段总和,则将多出的部分平均,否则丢弃这条数据;
  3. 重复执行,直到所有数据都处理完毕,最后在数据中加零。

结果可如下所示:

降水重建序列结果

0x03 代码实现

以下是 Python 代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import pandas as pd
import os

def read_xlsx(file_path):
"""
Read an Excel file and return a DataFrame.
"""
if os.path.exists(file_path):
df = pd.read_excel(file_path)
return df
else:
raise FileNotFoundError(f"The file {file_path} does not exist.")


def first_processing(df, t_begin_col, t_end_col, data_col):
"""
INPUT: pandas.DataFrame, t_begin_col, t_end_col, data_col
OUTPUT: pandas.DataFrame
"""
df[t_begin_col] = pd.to_datetime(df[t_begin_col])
df[t_end_col] = pd.to_datetime(df[t_end_col])

df = df[df[t_begin_col] <= df[t_end_col]].copy()

global_begin = df[t_begin_col].min()
global_end = df[t_end_col].max()

full_range = pd.date_range(start=global_begin, end=global_end, freq='h')

mapping = {}
for _, row in df.iterrows():
start = row[t_begin_col]
end = row[t_end_col]
value = row[data_col]
if end - start == pd.Timedelta(hours=1):
mapping[start] = value

new_rows = []
for ts in full_range:
new_rows.append({t_begin_col: ts, data_col: mapping.get(ts, 0),"type": 1 if ts in mapping else 0})

new_df = pd.DataFrame(new_rows)
return new_df


def further_processing(df, t_begin_col, t_end_col, data_col, target_df, type_val):
"""
INPUT: pandas.DataFrame, t_begin_col, t_end_col, data_col, target_df, type_val
OUTPUT: pandas.DataFrame
"""

interval = pd.Timedelta(hours=type_val)

filtered_df = df[(df[t_end_col] - df[t_begin_col] == interval)].drop_duplicates(
subset=[t_begin_col, t_end_col, data_col])

for _, row in filtered_df.iterrows():
current_time = row[t_begin_col]
selected_value = row[data_col]
period_start = current_time
period_end = current_time + interval


mask = (target_df[t_begin_col] >= period_start) & (target_df[t_begin_col] < period_end)
sub_df = target_df.loc[mask]


existing_nonzero_sum = sub_df[sub_df[data_col] != 0][data_col].sum()


remain_value = selected_value - existing_nonzero_sum

if remain_value > 0:

empty_mask = sub_df[data_col] == 0
empty_count = empty_mask.sum()
if empty_count > 0:
average_val = remain_value / empty_count
target_df.loc[mask & empty_mask, data_col] = average_val
target_df.loc[mask & empty_mask, "type"] = type_val
return target_df

def prep_extract(df,prep0,time,excel_name):
"""
INPUT: pandas.DataFrame, prep0,time,excel_name
OUTPUT: Excel file with sequences of rainfall
"""

results = []
current_sequence = []
current_type = None


for index, row in df.iterrows():
if row['prep'] > prep0:
current_sequence.append(row)
else:
if len(current_sequence) >= time:
results.append((current_sequence, current_type))
current_sequence = []

if len(current_sequence) >= time:
results.append((current_sequence, current_type))


with pd.ExcelWriter(excel_name) as writer:
for i, (sequence, seq_type) in enumerate(results):
sequence_df = pd.DataFrame(sequence)
sequence_df.to_excel(writer, sheet_name=f"Seq_{i+1}", index=False)


if __name__ == "__main__":

file_path = 'data.xlsx'
df = pd.read_excel(file_path)

t_begin_col = 'begin'
t_end_col = 'end'
data_col = 'prep'
types=[2,3,4,5,6,7,8,9,10,11,12,13,14,15]

hourly_df = first_processing(df, t_begin_col, t_end_col, data_col)
for i in types:
hourly_df = further_processing(df, t_begin_col, t_end_col, data_col, hourly_df, i)
hourly_df.to_excel('hourly.xlsx', index=False)
prep_extract(hourly_df,2,5,'prep_extract.xlsx')
print(hourly_df)

0x04 总结

  1. 基于历史天气数据,可以对降水量单位进行合理推断;
  2. 对不同时长的数据进行尽可能的取舍,最大化利用现有数据;
  3. 降雨时长普遍较短(< 5h),因此采用平均值是可接受的,接下来可根据历史降雨的概率分布重分配场次降雨以提高精度;
  4. 代码实现较为简单,主要是对数据进行处理和重建,实际应用中可能需要更多的异常处理和数据验证

0x05 参考资料

本文作者:SatyrLee
本文链接:http://www.naive514.top/posts/827fca76/
版权声明:本文采用 CC BY-NC-SA 4.0 CN 协议进行许可