oafuncs 0.0.98.10__py3-none-any.whl → 0.0.98.12__py3-none-any.whl

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.
@@ -4,7 +4,9 @@ from typing import List, Optional, Union
4
4
  import numpy as np
5
5
  import xarray as xr
6
6
  from dask.diagnostics import ProgressBar
7
+
7
8
  from oafuncs import pbar
9
+ from oafuncs._script.netcdf_write import _calculate_scale_and_offset # 新增导入
8
10
 
9
11
 
10
12
  def merge_nc(file_list: Union[str, List[str]], var_name: Optional[Union[str, List[str]]] = None, dim_name: Optional[str] = None, target_filename: Optional[str] = None) -> None:
@@ -83,24 +85,19 @@ def merge_nc(file_list: Union[str, List[str]], var_name: Optional[Union[str, Lis
83
85
  encoding = {}
84
86
  for var in merged_dataset.data_vars:
85
87
  data = merged_dataset[var].values
86
- # print(f"Variable '{var}' ready for writing: min={data.min():.3f}, max={data.max():.3f}, mean={data.mean():.3f}")
87
88
  if data.dtype.kind in {"i", "u", "f"}: # 仅对数值型数据进行压缩
88
- data_range = data.max() - data.min()
89
- if data_range > 0: # 避免范围过小导致的精度问题
90
- scale_factor = data_range / (2**16 - 1)
91
- add_offset = data.min()
92
- encoding[var] = {
93
- "zlib": True,
94
- "complevel": 4,
95
- "dtype": "int16",
96
- "scale_factor": scale_factor,
97
- "add_offset": add_offset,
98
- "_FillValue": -32767,
99
- }
100
- else:
101
- encoding[var] = {"zlib": True, "complevel": 4} # 范围过小时禁用缩放
89
+ # 统一调用 netcdf_write 中的 scale/offset 计算
90
+ scale_factor, add_offset = _calculate_scale_and_offset(data, n=16)
91
+ encoding[var] = {
92
+ "zlib": True,
93
+ "complevel": 4,
94
+ "dtype": "int16",
95
+ "scale_factor": scale_factor,
96
+ "add_offset": add_offset,
97
+ "_FillValue": -32767,
98
+ }
102
99
  else:
103
- encoding[var] = {"zlib": True, "complevel": 4} # 非数值型数据不使用缩放
100
+ encoding[var] = {"zlib": True, "complevel": 4}
104
101
 
105
102
  # 确保写入时不会因编码问题导致数据丢失
106
103
  # merged_dataset.to_netcdf(target_filename, encoding=encoding)
oafuncs/oa_data.py CHANGED
@@ -121,7 +121,26 @@ def _interp_single_worker(*args):
121
121
  """
122
122
  data_slice, origin_points, target_points, interpolation_method, target_shape = args
123
123
 
124
- return griddata(origin_points, data_slice.ravel(), target_points, method=interpolation_method).reshape(target_shape)
124
+ # 过滤掉包含 NaN 的点
125
+ valid_mask = ~np.isnan(data_slice.ravel())
126
+ valid_data = data_slice.ravel()[valid_mask]
127
+ valid_points = origin_points[valid_mask]
128
+
129
+ if len(valid_data) < 10: # 如果有效数据太少,用均值填充
130
+ return np.full(target_shape, np.nanmean(data_slice))
131
+
132
+ # 使用有效数据进行插值
133
+ result = griddata(valid_points, valid_data, target_points, method=interpolation_method)
134
+ result = result.reshape(target_shape)
135
+
136
+ # 检查插值结果中是否仍有 NaN,如果有,用最近邻插值填充
137
+ if np.any(np.isnan(result)):
138
+ # 使用最近邻方法填充剩余的 NaN 值
139
+ nan_mask = np.isnan(result)
140
+ result_nn = griddata(valid_points, valid_data, target_points[nan_mask.ravel()], method="nearest")
141
+ result.ravel()[nan_mask.ravel()] = result_nn
142
+
143
+ return result
125
144
 
126
145
 
127
146
  def interp_2d(
@@ -130,7 +149,7 @@ def interp_2d(
130
149
  source_x_coordinates: Union[np.ndarray, List[float]],
131
150
  source_y_coordinates: Union[np.ndarray, List[float]],
132
151
  source_data: np.ndarray,
133
- interpolation_method: str = "linear",
152
+ interpolation_method: str = "cubic",
134
153
  ) -> np.ndarray:
135
154
  """
136
155
  Perform 2D interpolation on the last two dimensions of a multi-dimensional array.
@@ -141,7 +160,8 @@ def interp_2d(
141
160
  source_x_coordinates (Union[np.ndarray, List[float]]): Original grid's x-coordinates.
142
161
  source_y_coordinates (Union[np.ndarray, List[float]]): Original grid's y-coordinates.
143
162
  source_data (np.ndarray): Multi-dimensional array with the last two dimensions as spatial.
144
- interpolation_method (str, optional): Interpolation method. Defaults to "linear".
163
+ interpolation_method (str, optional): Interpolation method. Defaults to "cubic".
164
+ >>> optional: 'linear', 'nearest', 'cubic', 'quintic', etc.
145
165
  use_parallel (bool, optional): Enable parallel processing. Defaults to True.
146
166
 
147
167
  Returns:
@@ -190,12 +210,12 @@ def interp_2d(
190
210
  for t_index in range(t):
191
211
  for z_index in range(z):
192
212
  paras.append((new_src_data[t_index, z_index], origin_points, target_points, interpolation_method, target_shape))
193
-
213
+
194
214
  with PEx() as excutor:
195
215
  result = excutor.run(_interp_single_worker, paras)
196
- excutor.shutdown()
197
216
 
198
- return np.squeeze(np.array(result))
217
+ return np.squeeze(np.array(result).reshape(t, z, *target_shape))
218
+
199
219
 
200
220
 
201
221
  def mask_shapefile(
@@ -584,7 +584,7 @@ def _clear_existing_file(file_full_path):
584
584
 
585
585
  def _check_existing_file(file_full_path, avg_size):
586
586
  if os.path.exists(file_full_path):
587
- print(f"[bold #FFA54F]{file_full_path} exists")
587
+ print(f"[bold #FFA54F]{file_full_path} exists ...")
588
588
  fsize = file_size(file_full_path)
589
589
  delta_size_ratio = (fsize - avg_size) / avg_size
590
590
  if abs(delta_size_ratio) > 0.025:
@@ -796,7 +796,7 @@ def _download_file(target_url, store_path, file_name, cover=False):
796
796
  get_mean_size = _get_mean_size_move(same_file, save_path)
797
797
 
798
798
  if _check_existing_file(save_path, get_mean_size):
799
- print(f"[bold #FFA54F]{save_path} exists, skipping ...")
799
+ # print(f"[bold #FFA54F]{save_path} exists, skipping ...")
800
800
  count_dict["skip"] += 1
801
801
  return
802
802
 
@@ -901,7 +901,7 @@ def _prepare_url_to_download(var, lon_min=0, lon_max=359.92, lat_min=-80, lat_ma
901
901
  else:
902
902
  if download_time < "2024081012":
903
903
  varlist = [_ for _ in var]
904
- for key, value in pbar(var_group.items(), description=f"Var Group {download_time} ->", total=len(var_group), color="#d7feb9", next_line=True):
904
+ for key, value in pbar(var_group.items(), description=f"Var Group {download_time}", total=len(var_group), next_line=True):
905
905
  current_group = []
906
906
  for v in varlist:
907
907
  if v in value:
@@ -923,7 +923,7 @@ def _prepare_url_to_download(var, lon_min=0, lon_max=359.92, lat_min=-80, lat_ma
923
923
  file_name = f"HYCOM_{key}_{download_time}-{download_time_end}.nc"
924
924
  _download_file(submit_url, store_path, file_name, cover)
925
925
  else:
926
- for v in pbar(var, description=f"Var {download_time} ->", total=len(var), color="#d7feb9", next_line=True):
926
+ for v in pbar(var, description=f"Var {download_time}", total=len(var), next_line=True):
927
927
  submit_url = _get_submit_url_var(v, depth, level_num, lon_min, lon_max, lat_min, lat_max, dataset_name, version_name, download_time, download_time_end)
928
928
  file_name = f"HYCOM_{variable_info[v]['var_name']}_{download_time}.nc"
929
929
  if download_time_end is not None:
@@ -968,7 +968,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
968
968
  # with Progress() as progress:
969
969
  # task = progress.add_task(f"[cyan]{bar_desc}", total=len(time_list))
970
970
  if num_workers is None or num_workers <= 1:
971
- for i, time_str in pbar(enumerate(time_list), description=f"{bar_desc}", total=len(time_list), cmap='colorful_1', next_line=True):
971
+ for i, time_str in pbar(enumerate(time_list), description=f"{bar_desc}", total=len(time_list), next_line=True):
972
972
  _prepare_url_to_download(var, lon_min, lon_max, lat_min, lat_max, time_str, None, depth, level, store_path, dataset_name, version_name, cover)
973
973
  # progress.update(task, advance=1, description=f"[cyan]{bar_desc} {i + 1}/{len(time_list)}")
974
974
  else:
@@ -976,7 +976,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
976
976
  futures = [executor.submit(_download_task, var, time_str, None, lon_min, lon_max, lat_min, lat_max, depth, level, store_path, dataset_name, version_name, cover) for time_str in time_list]
977
977
  """ for feature in as_completed(futures):
978
978
  _done_callback(feature, progress, task, len(time_list), counter_lock) """
979
- for _ in pbar(as_completed(futures),description=f"{bar_desc}", total=len(futures),cmap='colorful_1',next_line=True):
979
+ for _ in pbar(as_completed(futures),description=f"{bar_desc}", total=len(futures),next_line=True):
980
980
  pass
981
981
  else:
982
982
  print("[bold red]Please ensure the time_s is no more than time_e")
@@ -1121,10 +1121,10 @@ def download(
1121
1121
  workers = 1
1122
1122
  given_idm_engine = idm_path
1123
1123
  idm_download_list = []
1124
- bar_desc = "Submitting to IDM ->"
1124
+ bar_desc = "Submitting to IDM"
1125
1125
  else:
1126
1126
  use_idm = False
1127
- bar_desc = "Downloading ->"
1127
+ bar_desc = "Downloading"
1128
1128
 
1129
1129
  global match_time
1130
1130
  match_time = validate_time
@@ -1136,7 +1136,7 @@ def download(
1136
1136
  workers = 1
1137
1137
  print("*" * mark_len)
1138
1138
  print("[bold red]Only checking the time of existing files.")
1139
- bar_desc = "Checking time ->"
1139
+ bar_desc = "Checking time"
1140
1140
 
1141
1141
  _download_hourly_func(
1142
1142
  variables,
@@ -1162,7 +1162,7 @@ def download(
1162
1162
  print("[bold #ecdbfe]*" * mark_len)
1163
1163
  if idm_download_list:
1164
1164
  remain_list = idm_download_list.copy()
1165
- for _ in pbar(range(len(idm_download_list)), cmap="diverging_1", description="Downloading ->"):
1165
+ for _ in pbar(range(len(idm_download_list)), description="Downloading"):
1166
1166
  success = False
1167
1167
  while not success:
1168
1168
  for f in remain_list:
oafuncs/oa_nc.py CHANGED
@@ -6,7 +6,7 @@ import numpy as np
6
6
  import xarray as xr
7
7
  from rich import print
8
8
 
9
- __all__ = ["save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw", "unpack_netcdf"]
9
+ __all__ = ["save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw", "compress_netcdf", "unpack_netcdf"]
10
10
 
11
11
 
12
12
  def save(
@@ -278,6 +278,26 @@ def draw(
278
278
  print("[red]No dataset or file provided.[/red]")
279
279
 
280
280
 
281
+ def compress_netcdf(src_path, dst_path=None):
282
+ """
283
+ 压缩 NetCDF 文件,使用 scale_factor/add_offset 压缩数据。
284
+ 若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
285
+ """
286
+ # 判断是否要替换原文件
287
+ delete_orig = dst_path is None
288
+ if delete_orig:
289
+ dst_path = src_path.replace(".nc", "_compress.nc")
290
+
291
+ ds = xr.open_dataset(src_path)
292
+ save(dst_path, ds)
293
+ ds.close()
294
+
295
+ if delete_orig:
296
+ os.remove(src_path)
297
+ os.rename(dst_path, src_path)
298
+ pass
299
+
300
+
281
301
  def unpack_netcdf(src_path, dst_path=None):
282
302
  """解码 NetCDF 并移除 scale_factor/add_offset,写出真实值。
283
303
  若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.10
3
+ Version: 0.0.98.12
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -1,18 +1,18 @@
1
1
  oafuncs/__init__.py,sha256=T_-VtnWWllV3Q91twT5Yt2sUapeA051QbPNnBxmg9nw,1456
2
2
  oafuncs/oa_cmap.py,sha256=DimWT4Bg7uE5Lx8hSw1REp7whpsR2pFRStAwk1cowEM,11494
3
- oafuncs/oa_data.py,sha256=F0IR7T-BoWZho5aoDI_mWWUuuOvifohkkNLFm9Wlsqs,10312
3
+ oafuncs/oa_data.py,sha256=PXn4EpSbLPHhYmNJXEStd8vIMwInl3a9O9sW2c9z-g0,11152
4
4
  oafuncs/oa_date.py,sha256=WhM6cyD4G3IeghjLTHhAMtlvJbA7kwQG2sHnxdTgyso,6303
5
5
  oafuncs/oa_draw.py,sha256=Wj2QBgyIPpV_dxaDrH10jqj_puK9ZM9rd-si-3VrsrE,17631
6
6
  oafuncs/oa_file.py,sha256=j9gXJgPOJsliu4IOUc4bc-luW4yBvQyNCEmMyDVjUwQ,16404
7
7
  oafuncs/oa_help.py,sha256=_4AZgRDq5Or0vauNvq5IDDHIBoBfdOQtzak-mG1wwAw,4537
8
- oafuncs/oa_nc.py,sha256=S23QL_GfIaENPr9p7oEeFT34nqJ2-7fiCcFQu72CmjI,11327
8
+ oafuncs/oa_nc.py,sha256=lLEPjj4qgdEw1al0r1nKGZUnMP_ejT8A2NKD4lrs2kc,11936
9
9
  oafuncs/oa_python.py,sha256=NkopwkYFGSEuVljnTBvXCl6o2CeyRNBqRXSsUl3euEE,5192
10
10
  oafuncs/oa_tool.py,sha256=EqOlGPq3Rx2ohqVnGuCZhMvr2o9_XgglrETMbAdEifM,8471
11
11
  oafuncs/_data/hycom.png,sha256=MadKs6Gyj5n9-TOu7L4atQfTXtF9dvN9w-tdU9IfygI,10945710
12
12
  oafuncs/_data/oafuncs.png,sha256=o3VD7wm-kwDea5E98JqxXl04_78cBX7VcdUt7uQXGiU,3679898
13
13
  oafuncs/_script/cprogressbar.py,sha256=UIgGcLFs-6IgWlITuBLaQqrpt4OAK3Mst5RlCiNfZdQ,15772
14
14
  oafuncs/_script/email.py,sha256=lL4HGKrr524-g0xLlgs-4u7x4-u7DtgNoD9AL8XJKj4,3058
15
- oafuncs/_script/netcdf_merge.py,sha256=ktmTOgGfLHBNdS4HBc6xFDfO8B7E4DT7d1e6Dtare9Y,5596
15
+ oafuncs/_script/netcdf_merge.py,sha256=ncNxstXJ77Ftyac5b1yRriVoBxbTVtaU111NU_7k_QA,5282
16
16
  oafuncs/_script/netcdf_modify.py,sha256=sGRUYNhfGgf9JV70rnBzw3bzuTRSXzBTL_RMDnDPeLQ,4552
17
17
  oafuncs/_script/netcdf_write.py,sha256=iO1Qv9bp6RLiw1D8Nrv7tX_8X-diUZaX3Nxhk6pJ5Nw,8556
18
18
  oafuncs/_script/parallel.py,sha256=T9Aie-e4LcbKlFTLZ0l4lhEN3SBVa84jRcrAsIm8s0I,8767
@@ -21,7 +21,7 @@ oafuncs/_script/plot_dataset.py,sha256=zkSEnO_-biyagorwWXPoihts_cwuvripzEt-l9bHJ
21
21
  oafuncs/_script/replace_file_content.py,sha256=eCFZjnZcwyRvy6b4mmIfBna-kylSZTyJRfgXd6DdCjk,5982
22
22
  oafuncs/oa_down/User_Agent-list.txt,sha256=pHaMlElMvZ8TG4vf4BqkZYKqe0JIGkr4kCN0lM1Y9FQ,514295
23
23
  oafuncs/oa_down/__init__.py,sha256=kRX5eTUCbAiz3zTaQM1501paOYS_3fizDN4Pa0mtNUA,585
24
- oafuncs/oa_down/hycom_3hourly.py,sha256=wWV14-OB9_LMmjUiZr3YXWBdKKwAyGXNa3Up7fSiWwk,55553
24
+ oafuncs/oa_down/hycom_3hourly.py,sha256=VYxG9DPAnS1wk8gEXyqSIww4IvNRWOYGC4D6PTh5t3A,55450
25
25
  oafuncs/oa_down/hycom_3hourly_proxy.py,sha256=1eaoJGI_m-7w4ZZ3n7NGxkZaeFdsm0d3U-hyw8RFNbc,54563
26
26
  oafuncs/oa_down/idm.py,sha256=4z5IvgfTyIKEI1kOtqXZwN7Jnfjwp6qDBOIoVyOLp0I,1823
27
27
  oafuncs/oa_down/literature.py,sha256=2bF9gSKQbzcci9LcKE81j8JEjIJwON7jbwQB3gDDA3E,11331
@@ -37,8 +37,8 @@ oafuncs/oa_sign/__init__.py,sha256=QKqTFrJDFK40C5uvk48GlRRbGFzO40rgkYwu6dYxatM,5
37
37
  oafuncs/oa_sign/meteorological.py,sha256=8091SHo2L8kl4dCFmmSH5NGVHDku5i5lSiLEG5DLnOQ,6489
38
38
  oafuncs/oa_sign/ocean.py,sha256=xrW-rWD7xBWsB5PuCyEwQ1Q_RDKq2KCLz-LOONHgldU,5932
39
39
  oafuncs/oa_sign/scientific.py,sha256=a4JxOBgm9vzNZKpJ_GQIQf7cokkraV5nh23HGbmTYKw,5064
40
- oafuncs-0.0.98.10.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
41
- oafuncs-0.0.98.10.dist-info/METADATA,sha256=iyv12KtjFAqYtbJxBTq3RhgQ55iG5_yfCx4IzVWfJHw,4273
42
- oafuncs-0.0.98.10.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
43
- oafuncs-0.0.98.10.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
44
- oafuncs-0.0.98.10.dist-info/RECORD,,
40
+ oafuncs-0.0.98.12.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
41
+ oafuncs-0.0.98.12.dist-info/METADATA,sha256=TfDApyqtzs-wBr9sBG2sYBVHZFfJht6g1N3JtCQSHfU,4273
42
+ oafuncs-0.0.98.12.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
43
+ oafuncs-0.0.98.12.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
44
+ oafuncs-0.0.98.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5