Alaea 2.4.0__tar.gz → 2.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {alaea-2.4.0 → alaea-2.4.2}/.DS_Store +0 -0
  2. {alaea-2.4.0 → alaea-2.4.2}/Alaea/S_DateTime.py +2 -2
  3. {alaea-2.4.0 → alaea-2.4.2}/Alaea/baysalt/erosion_coastline.py +209 -100
  4. {alaea-2.4.0 → alaea-2.4.2/Alaea.egg-info}/PKG-INFO +1 -1
  5. {alaea-2.4.0/Alaea.egg-info → alaea-2.4.2}/PKG-INFO +1 -1
  6. {alaea-2.4.0 → alaea-2.4.2}/setup.py +1 -1
  7. {alaea-2.4.0 → alaea-2.4.2}/.gitattributes +0 -0
  8. {alaea-2.4.0 → alaea-2.4.2}/.gitignore +0 -0
  9. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Blogging.py +0 -0
  10. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Pyshell/RS_File.py +0 -0
  11. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Pyshell/__init__.py +0 -0
  12. {alaea-2.4.0 → alaea-2.4.2}/Alaea/SameAsMATLAB/CoorTrans.py +0 -0
  13. {alaea-2.4.0 → alaea-2.4.2}/Alaea/SameAsMATLAB/__init__.py +0 -0
  14. {alaea-2.4.0 → alaea-2.4.2}/Alaea/SameAsMATLAB/calculatefuns.py +0 -0
  15. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Standard/__init__.py +0 -0
  16. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Standard/xr_attrs.py +0 -0
  17. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Standard/xr_ice.py +0 -0
  18. {alaea-2.4.0 → alaea-2.4.2}/Alaea/Standard/xr_wave.py +0 -0
  19. {alaea-2.4.0 → alaea-2.4.2}/Alaea/__init__.py +0 -0
  20. {alaea-2.4.0 → alaea-2.4.2}/Alaea/baysalt/Forage/__init__.py +0 -0
  21. {alaea-2.4.0 → alaea-2.4.2}/Alaea/baysalt/Forage/erosion_data/__init__.py +0 -0
  22. {alaea-2.4.0 → alaea-2.4.2}/Alaea/baysalt/__init__.py +0 -0
  23. {alaea-2.4.0 → alaea-2.4.2}/Alaea/commonCode.py +0 -0
  24. {alaea-2.4.0 → alaea-2.4.2}/Alaea/cprintfs.py +0 -0
  25. {alaea-2.4.0 → alaea-2.4.2}/Alaea/exutils/Interp/Interp_g2s.py +0 -0
  26. {alaea-2.4.0 → alaea-2.4.2}/Alaea/exutils/Interp/Interp_s2g.py +0 -0
  27. {alaea-2.4.0 → alaea-2.4.2}/Alaea/exutils/Interp/__init__.py +0 -0
  28. {alaea-2.4.0 → alaea-2.4.2}/Alaea/exutils/__init__.py +0 -0
  29. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/__init__.py +0 -0
  30. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/fvcom_class.py +0 -0
  31. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/fvcom_image.py +0 -0
  32. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/fvcom_load.py +0 -0
  33. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/fvcom_tools.py +0 -0
  34. {alaea-2.4.0 → alaea-2.4.2}/Alaea/pFVCOM/writeFile.py +0 -0
  35. {alaea-2.4.0 → alaea-2.4.2}/Alaea/processBar.py +0 -0
  36. {alaea-2.4.0 → alaea-2.4.2}/Alaea/read_conf.py +0 -0
  37. {alaea-2.4.0 → alaea-2.4.2}/Alaea/server_info.py +0 -0
  38. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/P_params.py +0 -0
  39. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/Read.py +0 -0
  40. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/__init__.py +0 -0
  41. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/download_check.py +0 -0
  42. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/geo_distance.py +0 -0
  43. {alaea-2.4.0 → alaea-2.4.2}/Alaea/tools/kml2shp.py +0 -0
  44. {alaea-2.4.0 → alaea-2.4.2}/Alaea/warningTools/__init__.py +0 -0
  45. {alaea-2.4.0 → alaea-2.4.2}/Alaea.egg-info/SOURCES.txt +0 -0
  46. {alaea-2.4.0 → alaea-2.4.2}/Alaea.egg-info/dependency_links.txt +0 -0
  47. {alaea-2.4.0 → alaea-2.4.2}/Alaea.egg-info/requires.txt +0 -0
  48. {alaea-2.4.0 → alaea-2.4.2}/Alaea.egg-info/top_level.txt +0 -0
  49. {alaea-2.4.0 → alaea-2.4.2}/MANIFEST.in +0 -0
  50. {alaea-2.4.0 → alaea-2.4.2}/README.md +0 -0
  51. {alaea-2.4.0 → alaea-2.4.2}/run_twine.py +0 -0
  52. {alaea-2.4.0 → alaea-2.4.2}/setup.cfg +0 -0
Binary file
@@ -123,9 +123,9 @@ class Ttime:
123
123
 
124
124
  if TIME is not None and fmt is not None:
125
125
  if isinstance(TIME, list):
126
- self.Times = [datetime.datetime.strptime(TIME[i], fmt) for i in range(len(TIME))]
126
+ self.Times = [datetime.strptime(TIME[i], fmt) for i in range(len(TIME))]
127
127
  else:
128
- self.Times = [datetime.datetime.strptime(TIME, fmt)]
128
+ self.Times = [datetime.strptime(TIME, fmt)]
129
129
  elif TIME is not None:
130
130
  if hasattr(TIME[0], 'dtype'):
131
131
  if isinstance(TIME, list):
@@ -14,10 +14,11 @@
14
14
 
15
15
  这里的“erosion(侵蚀)”更接近一种海岸线附近的掩膜修正 / 邻域填补:
16
16
  """
17
-
18
- import itertools
17
+ import contextlib
18
+ import os
19
19
  import time
20
- from collections import deque
20
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
21
+ from typing import List
21
22
 
22
23
  import matplotlib.pyplot as plt
23
24
  import numpy as np
@@ -30,9 +31,13 @@ from Alaea.Blogging import Blog
30
31
  from Alaea.tools.Read import read_json, write_json
31
32
 
32
33
  __all__ = [
34
+ 'LOGGER',
33
35
  'erosion_cal_id',
34
36
  'erosion_via_id',
35
- 'LOGGER',
37
+ 'erosion_cal_id_multi_times',
38
+ 'erosion_via_id_multi_times',
39
+ 'erosion_via_id_multi_vars',
40
+ 'erosion_via_id_multi_times_multi_vars',
36
41
  ]
37
42
 
38
43
  BLOG = Blog('erosion_coastline')
@@ -47,7 +52,7 @@ def erosion_cal_id(
47
52
  k,
48
53
  judge_num,
49
54
  method='np.nan',
50
- ):
55
+ ) -> dict:
51
56
  """
52
57
  计算“哪些点需要被回填”,以及“每个点应该参考哪些邻居点”。
53
58
 
@@ -90,79 +95,73 @@ def erosion_cal_id(
90
95
  lon_mesh, lat_mesh = np.meshgrid(g_lon, g_lat)
91
96
  # 后面 KDTree 就是在这个二维坐标集合上做最近邻搜索
92
97
  point = np.column_stack((lon_mesh.ravel(), lat_mesh.ravel()))
93
- # 下面两个 deque 用来保存所有“候选邻居点”的二维索引
94
- fp_lon = deque([])
95
- fp_lat = deque([])
96
-
97
- # 建立 KDTree:适合大量最近邻查询
98
- # 这里 point 里装的是所有格点坐标,所以相当于“让每个格点去找离自己最近的 k 个格点”
99
- ckt = spt.cKDTree(point) # 用C写的查找类,执行速度更快
100
- # 这里不再逐点循环,而是一次性把所有格点的近邻都查出来
101
- LOGGER.debug('Kdtree建立完毕,开始查找每个格点的近邻点')
102
-
103
- # 对所有格点一次性查询最近邻:
104
- # distances.shape 约为 (n_points, k)
105
- # sequences.shape 约为 (n_points, k)
106
- # sequences 里保存的是最近邻在 point 数组中的一维编号
107
- distances, sequences = ckt.query(point, k)
108
-
109
- # point 中每个元素对应一个展开后的一维索引 [0, 1, 2, ...]
110
- idx_all = np.arange(point.shape[0])
111
- # 把展开索引还原回二维索引:
112
- # 行号 = index // 每行列数
113
- lat_i_all = idx_all // lon_number
114
- # 列号 = index % 每行列数
115
- lon_i_all = idx_all % lon_number
116
98
 
117
99
  # 先找出哪些格点属于“待处理点”
118
100
  # 不同数据源中,水点/无效点/边界点的表达方式不同,所以这里分三种判断方法
119
101
  if method == 'np.nan':
120
- mask = np.isnan(value[lat_i_all, lon_i_all])
102
+ mask = np.isnan(value)
121
103
  elif method == 'nc.mask':
122
- mask = value.mask[lat_i_all, lon_i_all]
104
+ mask = value.mask
123
105
  elif method == 'txt.mask':
124
- mask = (value[lat_i_all, lon_i_all] == 0)
106
+ mask = (value == 0)
125
107
  else:
126
- mask = np.zeros_like(lat_i_all, dtype=bool)
108
+ mask = np.zeros((lat_number, lon_number), dtype=bool)
127
109
 
128
- # 只保留那些“需要处理”的点在 point 里的编号
129
- valid_idx = idx_all[mask]
110
+ target_lat, target_lon = np.where(mask)
111
+ if target_lat.size == 0:
112
+ return {
113
+ 'change_lat': [],
114
+ 'change_lon': [],
115
+ 'value_lat': [],
116
+ 'value_lon': [],
117
+ }
130
118
 
131
- # 取出这些目标点各自对应的 k 个最近邻编号
132
- neigh = sequences[valid_idx]
119
+ valid_idx = target_lat * lon_number + target_lon
120
+ target_point = point[valid_idx]
133
121
 
134
- # 展平后,后面统一换算成二维索引
135
- neigh = neigh.reshape(-1)
122
+ # 建立 KDTree:适合大量最近邻查询
123
+ ckt = spt.cKDTree(point) # 用C写的查找类,执行速度更快
124
+ LOGGER.debug('Kdtree建立完毕,开始查找待处理格点的近邻点')
136
125
 
137
- # 邻居点的一维编号 -> 二维行列编号
138
- lat_j = neigh // lon_number
139
- lon_j = neigh % lon_number
126
+ # 只对待处理点查找最近邻,而不是对所有格点都查
127
+ query_kwargs = {'k': k}
128
+ with contextlib.suppress(Exception):
129
+ query_kwargs['workers'] = -1
130
+ _, neigh = ckt.query(target_point, **query_kwargs)
140
131
 
141
- # 这里会把所有目标点的 k 个邻居顺序拼在一起
142
- # 因此后面需要再按 k reshape 回去,恢复“每 k 个属于同一个目标点”的结构
143
- fp_lat.extend(lat_j.tolist())
144
- fp_lon.extend(lon_j.tolist())
145
- LOGGER.debug('所有格点的近邻点查找完毕,开始获取行列索引')
132
+ # 当 k == 1 时,scipy 会返回一维数组,这里统一成二维
133
+ neigh = np.atleast_2d(neigh)
134
+ if neigh.shape[0] != target_lat.size:
135
+ neigh = neigh.reshape(target_lat.size, -1)
146
136
 
147
- # 这里 fp_lat / fp_lon 已经是索引,不再需要 find_index
148
- lat_id = list(fp_lat)
149
- lon_id = list(fp_lon)
137
+ # 邻居点的一维编号 -> 二维行列编号
138
+ lat_id = neigh // lon_number
139
+ lon_id = neigh % lon_number
150
140
 
151
141
  LOGGER.debug('行列索引获取完毕,开始判断每个目标点的邻居里有效点数量')
152
-
142
+
153
143
  # 根据不同类型的数据,进一步判断:
154
144
  # 某个目标点的 k 个邻居里,是否有足够多的有效点可以拿来回填
155
- lonli_arr = np.array([0])
156
- latli_arr = np.array([0])
157
145
  if method == 'np.nan':
158
146
  latli_arr, lonli_arr = judge_nan_npnan(lat_id, lon_id, value, k, judge_num)
159
- elif method =='nc.mask':
147
+ elif method == 'nc.mask':
160
148
  latli_arr, lonli_arr = judge_nan_ncmask(lat_id, lon_id, value, k, judge_num)
161
149
  elif method == 'txt.mask':
162
150
  latli_arr, lonli_arr = judge_nan_txtmask(lat_id, lon_id, value, k, judge_num)
151
+ else:
152
+ latli_arr = np.empty((0, k), dtype=int)
153
+ lonli_arr = np.empty((0, k), dtype=int)
163
154
 
164
155
  LOGGER.debug('有效邻居点数量判断完毕,开始生成索引')
165
156
 
157
+ if latli_arr.size == 0:
158
+ return {
159
+ 'change_lat': [],
160
+ 'change_lon': [],
161
+ 'value_lat': [],
162
+ 'value_lon': [],
163
+ }
164
+
166
165
  # reshape 之后,每一行对应“一个目标点 + 它的 k-1 或 k 个邻居”
167
166
  # 约定每行第 1 列是目标点本身,后面各列是参考邻居
168
167
  _change_lat = latli_arr[:, 0].tolist()
@@ -191,27 +190,23 @@ def judge_nan_ncmask(latli, lonli, value, k, judge_num):
191
190
  - 如果这 k 个点里“未被 mask 的有效点”数量不少于 judge_num,
192
191
  就保留这一组索引
193
192
  """
194
- # 先转成数组,便于 reshape 成 [目标点数, k] 的二维结构
195
- latli_1 = np.array(latli)
196
- lonli_1 = np.array(lonli)
197
- # 每一行表示一个目标点对应的 k 个最近邻索引
198
- latli_2 = np.reshape(latli_1, [-1, k])
199
- lonli_2 = np.reshape(lonli_1, [-1, k])
193
+ latli_2 = np.asarray(latli)
194
+ lonli_2 = np.asarray(lonli)
195
+ if latli_2.size == 0:
196
+ return latli_2.reshape(0, k), lonli_2.reshape(0, k)
197
+
200
198
  # mask=True 说明该位置是无效/被遮罩的点
201
199
  mask_arr = value.mask
202
200
  # 取出每组 k 个邻居的 mask 情况
203
- test_arr = mask_arr[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
201
+ test_arr = mask_arr[latli_2, lonli_2]
204
202
 
205
203
  # 统计每组中“无效点”的数量
206
- sum_arr = np.sum(test_arr, axis=1) #
204
+ sum_arr = np.sum(test_arr, axis=1)
207
205
 
208
206
  # 无效点数 <= k - judge_num,等价于有效点数 >= judge_num
209
- select_id = np.where(sum_arr <= k-judge_num)[0].tolist()
207
+ select_mask = sum_arr <= (k - judge_num)
210
208
 
211
- latli_r = latli_2[select_id[:]]
212
- lonli_r = lonli_2[select_id[:]]
213
-
214
- return latli_r, lonli_r
209
+ return latli_2[select_mask], lonli_2[select_mask]
215
210
 
216
211
 
217
212
  def judge_nan_npnan(latli, lonli, value, k, judge_num):
@@ -221,26 +216,22 @@ def judge_nan_npnan(latli, lonli, value, k, judge_num):
221
216
  逻辑与 judge_nan_ncmask 一样,只不过这里通过 np.isnan(value)
222
217
  判断某个点是否无效。
223
218
  """
224
- # 每 k 个索引是一组,后面要恢复成二维结构
225
- latli_1 = np.array(latli)
226
- lonli_1 = np.array(lonli)
227
- # reshape 后每一行对应 1 个目标点的 k 个邻居
228
- latli_2 = np.reshape(latli_1, [-1, k])
229
- lonli_2 = np.reshape(lonli_1, [-1, k])
219
+ latli_2 = np.asarray(latli)
220
+ lonli_2 = np.asarray(lonli)
221
+ if latli_2.size == 0:
222
+ return latli_2.reshape(0, k), lonli_2.reshape(0, k)
223
+
230
224
  # NaN 为 True,表示该点不能直接作为有效值参与平均
231
225
  mask_arr = np.isnan(value)
232
226
 
233
227
  # 检查每组 k 个邻居中哪些位置是 NaN
234
- test_arr = mask_arr[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
235
- sum_arr = np.sum(test_arr, axis=1) #
228
+ test_arr = mask_arr[latli_2, lonli_2]
229
+ sum_arr = np.sum(test_arr, axis=1)
236
230
 
237
231
  # 只有有效值数量 >= judge_num 的目标点,才保留下来
238
- select_id = np.where(sum_arr <= k-judge_num)[0].tolist()
232
+ select_mask = sum_arr <= (k - judge_num)
239
233
 
240
- latli_r = latli_2[select_id[:]]
241
- lonli_r = lonli_2[select_id[:]]
242
-
243
- return latli_r, lonli_r
234
+ return latli_2[select_mask], lonli_2[select_mask]
244
235
 
245
236
 
246
237
  def judge_nan_txtmask(latli, lonli, value, k, judge_num):
@@ -253,25 +244,21 @@ def judge_nan_txtmask(latli, lonli, value, k, judge_num):
253
244
 
254
245
  当一组邻居中陆点数量 >= judge_num 时,保留该组。
255
246
  """
256
- # 同样先恢复成 [目标点数, k] 的结构
257
- latli_1 = np.array(latli)
258
- lonli_1 = np.array(lonli)
259
- latli_2 = np.reshape(latli_1, [-1, k])
260
- lonli_2 = np.reshape(lonli_1, [-1, k])
247
+ latli_2 = np.asarray(latli)
248
+ lonli_2 = np.asarray(lonli)
249
+ if latli_2.size == 0:
250
+ return latli_2.reshape(0, k), lonli_2.reshape(0, k)
261
251
 
262
252
  # 取出每组邻居的实际值;在 mask 中通常 0=水,1=陆
263
- test_arr = value[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
253
+ test_arr = value[latli_2, lonli_2]
264
254
 
265
255
  # 每组求和,相当于统计这一组里有多少个陆点
266
- sum_arr = np.sum(test_arr, axis=1) #
256
+ sum_arr = np.sum(test_arr, axis=1)
267
257
 
268
258
  # 只有陆点数达到阈值,才把该目标点纳入后续腐蚀/填补
269
- select_id = np.where(sum_arr >= judge_num)[0].tolist()
270
-
271
- latli_r = latli_2[select_id[:]]
272
- lonli_r = lonli_2[select_id[:]]
259
+ select_mask = sum_arr >= judge_num
273
260
 
274
- return latli_r, lonli_r
261
+ return latli_2[select_mask], lonli_2[select_mask]
275
262
 
276
263
 
277
264
  def erosion_via_id(_weight, value):
@@ -286,6 +273,7 @@ def erosion_via_id(_weight, value):
286
273
  支持两种输入:
287
274
  - 2D: [lat, lon]
288
275
  - 3D: [time, lat, lon]
276
+ 对 3D 输入会一次性批量处理整个 time 维,而不是逐时次循环。
289
277
 
290
278
  :param _weight: erosion_cal_id 返回的字典
291
279
  :param value: 二维或三维格点值
@@ -298,30 +286,151 @@ def erosion_via_id(_weight, value):
298
286
  _value_lat = _weight['value_lat']
299
287
  _value_lon = _weight['value_lon']
300
288
 
289
+ if len(_change_lat) == 0:
290
+ return value.copy()
291
+
301
292
  # 拷贝一份,避免直接改原数组
302
293
  new = value.copy()
294
+
295
+ # 统一转成整数索引数组:
296
+ # value_lat_arr / value_lon_arr 形状约为 [n_target, n_neighbor]
297
+ # change_lat_arr / change_lon_arr 形状约为 [n_target]
298
+ # 用 np.intp 可避免高级索引时的额外类型转换
299
+ value_lat_arr = np.asarray(_value_lat, dtype=np.intp)
300
+ value_lon_arr = np.asarray(_value_lon, dtype=np.intp)
301
+ change_lat_arr = np.asarray(_change_lat, dtype=np.intp)
302
+ change_lon_arr = np.asarray(_change_lon, dtype=np.intp)
303
+
303
304
  # 3D 情况:常见于 [time, lat, lon]
304
305
  if value.ndim == 3:
305
- for i in range(value.shape[0]):
306
- # 取出当前时刻下,每个目标点对应的一组邻居值
307
- replace = value[i, _value_lat, _value_lon]
308
- # 对每组邻居求平均,得到每个目标点的新值
309
- replace = np.nanmean(replace, axis=1)
310
- # 写回当前时刻的目标点位置
311
- new[i, _change_lat, _change_lon] = replace[:]
306
+ # 一次性取出所有时刻、所有目标点对应的邻居值,形状约为 [time, n_target, n_neighbor]
307
+ replace = value[:, value_lat_arr, value_lon_arr]
308
+ # 沿邻居维求平均,得到每个时刻、每个目标点的新值
309
+ replace = np.nanmean(replace, axis=2)
310
+ # 一次性写回,避免 Python 层逐时次循环
311
+ new[:, change_lat_arr, change_lon_arr] = replace
312
312
 
313
313
  # 2D 情况:直接对一个平面进行处理
314
314
  elif value.ndim == 2:
315
315
  # 取出每个目标点对应的一组邻居值
316
- replace = value[_value_lat, _value_lon]
316
+ replace = value[value_lat_arr, value_lon_arr]
317
317
  # 每组邻居求平均
318
318
  replace = np.nanmean(replace, axis=1)
319
319
  # 把平均值写回目标点
320
- new[_change_lat, _change_lon] = replace[:]
320
+ new[change_lat_arr, change_lon_arr] = replace
321
321
 
322
322
  return new
323
323
 
324
324
 
325
+ def erosion_cal_id_multi_times(_times: int,
326
+ g_lon,
327
+ g_lat,
328
+ value,
329
+ k,
330
+ judge_num,
331
+ method='np.nan',
332
+ ) -> List:
333
+ """
334
+ 多次迭代执行 erosion_cal_id,适用于需要连续多次回填的情况
335
+ :param _times: 迭代次数
336
+ :param g_lon: 经度数组 传递给 erosion_cal_id 的参数
337
+ :param g_lat: 纬度数组 传递给 erosion_cal_id 的参数
338
+ :param value: 初始格点值 传递给 erosion_cal_id 的参数
339
+ :param k: 每次 erosion_cal_id 传递的参数
340
+ :param judge_num: 每次 erosion_cal_id 传递的参数
341
+ :param method: 每次 erosion_cal_id 传递的参数
342
+ :return: 每次迭代生成的索引列表
343
+ """
344
+ weights = []
345
+ for _ in range(_times):
346
+ weight = erosion_cal_id(g_lon, g_lat, value, k, judge_num, method)
347
+ weights.append(weight)
348
+ # 每次迭代后,更新 value,供下一次迭代使用
349
+ value = erosion_via_id(weight, value)
350
+ return weights
351
+
352
+
353
+ def erosion_via_id_multi_times(weights: List,
354
+ value):
355
+ """
356
+ 根据 erosion_cal_id_multi_times 生成的索引列表,连续多次执行回填
357
+ :param weights: erosion_cal_id_multi_times 返回的索引列表
358
+ :param value: 初始的格点值,后续迭代会在上一次回填的基础上继续修改
359
+ """
360
+ new_value = value.copy()
361
+ for weight in weights:
362
+ new_value = erosion_via_id(weight, new_value)
363
+ return new_value
364
+
365
+ def erosion_via_id_multi_vars(weights: dict,
366
+ viss: dict,
367
+ _workers=-1,
368
+ _method_multi: str = 'thread',
369
+ ) -> dict:
370
+ """
371
+ 根据 erosion_cal_id_multi_times 生成的索引列表,对多个变量执行回填
372
+ :param weights: erosion_cal_id 返回的索引列表
373
+ :param viss: 字典,key 是变量名,value 是对应的格点值数组
374
+ :param _workers: 并行线程数,默认为 -1(使用所有可用线程)
375
+ :param _method_multi: 并行方法,'thread' 或 'process'
376
+ :return: 回填后的变量字典
377
+ """
378
+ keys = list(viss.keys())
379
+ key_count = len(keys)
380
+
381
+ if key_count == 0:
382
+ return {}
383
+ elif key_count == 1:
384
+ viss[keys[0]] = erosion_via_id(weights, viss[keys[0]])
385
+ elif key_count > 1:
386
+ cpu_count = os.cpu_count() or 1
387
+ # _workers cpu_count key_count 取最小值,避免过度并行导致性能下降
388
+ if _workers < 0:
389
+ _workers = min(cpu_count, key_count)
390
+ else:
391
+ _workers = min(_workers, cpu_count, key_count)
392
+
393
+ # 并行处理多个变量
394
+ if _method_multi == 'thread':
395
+ with ThreadPoolExecutor(max_workers=_workers) as executor:
396
+ futures = []
397
+ for key in keys:
398
+ future = executor.submit(erosion_via_id, weights, viss[key])
399
+ futures.append((key, future))
400
+ for key, future in futures:
401
+ viss[key] = future.result()
402
+
403
+ elif _method_multi == 'process':
404
+ with ProcessPoolExecutor(max_workers=_workers) as ex:
405
+ future_map = {
406
+ key: ex.submit(erosion_via_id, weights, viss[key])
407
+ for key in keys
408
+ }
409
+ for key in keys:
410
+ viss[key] = future_map[key].result()
411
+
412
+ return viss
413
+
414
+
415
+ def erosion_via_id_multi_times_multi_vars(weights: List,
416
+ viss: dict,
417
+ _workers=-1,
418
+ _method_multi: str = 'thread',
419
+ ) -> dict:
420
+ """
421
+ 根据 erosion_cal_id_multi_times 生成的索引列表,对多个变量连续多次执行回填
422
+ :param weights: erosion_cal_id_multi_times 返回的索引列表
423
+ :param viss: 字典,key 是变量名,value 是对应的格点值数组
424
+ :param _workers: 并行线程数,默认为 -1(使用所有可用线程)
425
+ :param _method_multi: 并行方法,'thread' 或 'process'
426
+ :return: 回填后的变量字典
427
+ """
428
+ new_viss = viss.copy()
429
+ for weight in weights:
430
+ new_viss = erosion_via_id_multi_vars(weight, new_viss, _workers=_workers, _method_multi=_method_multi)
431
+ return new_viss
432
+
433
+
325
434
  def example_cal_weight(methoood): # sourcery skip: extract-duplicate-method
326
435
  """
327
436
  示例函数:演示如何针对不同类型的数据源计算 weight,并执行一次回填。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Alaea
3
- Version: 2.4.0
3
+ Version: 2.4.2
4
4
  Summary: Some simple tools by Christmas
5
5
  Author: Christmas
6
6
  Author-email: 273519355@qq.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Alaea
3
- Version: 2.4.0
3
+ Version: 2.4.2
4
4
  Summary: Some simple tools by Christmas
5
5
  Author: Christmas
6
6
  Author-email: 273519355@qq.com
@@ -8,7 +8,7 @@ with open("README.md", "r") as fh:
8
8
 
9
9
  setuptools.setup(
10
10
  name="Alaea",
11
- version="2.4.0",
11
+ version="2.4.2",
12
12
  author="Christmas",
13
13
  author_email="273519355@qq.com",
14
14
  description="Some simple tools by Christmas",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes