oafuncs 0.0.80__py2.py3-none-any.whl → 0.0.82__py2.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.
Files changed (41) hide show
  1. oafuncs/__init__.py +27 -12
  2. oafuncs/oa_cmap.py +31 -52
  3. oafuncs/oa_data.py +107 -28
  4. oafuncs/oa_down/hycom_3hourly.py +151 -34
  5. oafuncs/oa_draw.py +148 -96
  6. oafuncs/oa_file.py +61 -50
  7. oafuncs/oa_nc.py +131 -53
  8. {oafuncs-0.0.80.dist-info → oafuncs-0.0.82.dist-info}/METADATA +1 -2
  9. oafuncs-0.0.82.dist-info/RECORD +24 -0
  10. oafuncs-0.0.82.dist-info/top_level.txt +1 -0
  11. oafuncs/oa_down/test.py +0 -151
  12. oafuncs/oa_s/__init__.py +0 -23
  13. oafuncs/oa_s/oa_cmap.py +0 -163
  14. oafuncs/oa_s/oa_data.py +0 -187
  15. oafuncs/oa_s/oa_draw.py +0 -451
  16. oafuncs/oa_s/oa_file.py +0 -332
  17. oafuncs/oa_s/oa_help.py +0 -39
  18. oafuncs/oa_s/oa_nc.py +0 -410
  19. oafuncs/oa_s/oa_python.py +0 -107
  20. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/__init__.py" +0 -26
  21. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_cmap.py" +0 -163
  22. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_data.py" +0 -187
  23. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/__init__.py" +0 -20
  24. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/hycom_3hourly.py" +0 -1176
  25. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/literature.py" +0 -332
  26. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/test_ua.py" +0 -151
  27. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_draw.py" +0 -451
  28. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_file.py" +0 -332
  29. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_help.py" +0 -39
  30. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_nc.py" +0 -410
  31. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_python.py" +0 -107
  32. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/__init__.py" +0 -21
  33. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/meteorological.py" +0 -168
  34. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/ocean.py" +0 -158
  35. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/scientific.py" +0 -139
  36. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/__init__.py" +0 -18
  37. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/email.py" +0 -114
  38. oafuncs-0.0.80.dist-info/RECORD +0 -51
  39. oafuncs-0.0.80.dist-info/top_level.txt +0 -2
  40. {oafuncs-0.0.80.dist-info → oafuncs-0.0.82.dist-info}/LICENSE.txt +0 -0
  41. {oafuncs-0.0.80.dist-info → oafuncs-0.0.82.dist-info}/WHEEL +0 -0
oafuncs/oa_file.py CHANGED
@@ -1,38 +1,39 @@
1
1
  #!/usr/bin/env python
2
2
  # coding=utf-8
3
- '''
3
+ """
4
4
  Author: Liu Kun && 16031215@qq.com
5
5
  Date: 2024-09-17 15:07:13
6
6
  LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2024-12-02 10:33:19
7
+ LastEditTime: 2024-12-13 16:28:56
8
8
  FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_file.py
9
9
  Description:
10
10
  EditPlatform: vscode
11
11
  ComputerInfo: XPS 15 9510
12
12
  SystemInfo: Windows 11
13
13
  Python Version: 3.11
14
- '''
15
-
14
+ """
16
15
 
17
16
  import glob
18
17
  import os
19
18
  import re
20
19
  import shutil
21
20
 
22
- __all__ = ['find_file', 'link_file', 'copy_file', 'rename_file', 'make_folder', 'clear_folder', 'remove_empty_folders', 'remove', 'file_size']
21
+ __all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folders", "remove", "file_size"]
23
22
 
24
23
 
25
- def find_file(parent_path, fname, mode='path'):
26
- '''
27
- description:
24
+ # ** 查找文件,支持通配符
25
+ def find_file(parent_path, fname, mode="path"):
26
+ """
27
+ description:
28
28
  param {*} parent_path: The parent path where the files are located
29
29
  param {*} fname: The file name pattern to search for
30
30
  param {*} mode: 'path' to return the full path of the files, 'file' to return only the file names
31
31
  return {*} A list of file paths or file names if files are found, None otherwise
32
- '''
32
+ """
33
+
33
34
  def natural_sort_key(s):
34
35
  """生成一个用于自然排序的键"""
35
- return [int(text) if text.isdigit() else text.lower() for text in re.split('([0-9]+)', s)]
36
+ return [int(text) if text.isdigit() else text.lower() for text in re.split("([0-9]+)", s)]
36
37
 
37
38
  # 将parent_path和fname结合成完整的搜索路径
38
39
  search_pattern = os.path.join(str(parent_path), fname)
@@ -48,7 +49,7 @@ def find_file(parent_path, fname, mode='path'):
48
49
  matched_files = sorted(matched_files, key=natural_sort_key)
49
50
 
50
51
  # 根据mode参数决定返回的内容
51
- if mode == 'file':
52
+ if mode == "file":
52
53
  # 只返回文件名
53
54
  result = [os.path.basename(file) for file in matched_files]
54
55
  else: # 默认为'path'
@@ -58,8 +59,9 @@ def find_file(parent_path, fname, mode='path'):
58
59
  return result
59
60
 
60
61
 
62
+ # ** 创建符号链接,支持通配符
61
63
  def link_file(src_pattern, dst):
62
- '''
64
+ """
63
65
  # 描述:创建符号链接,支持通配符
64
66
  # 使用示例
65
67
  # link_file(r'/data/hejx/liukun/era5/*', r'/data/hejx/liukun/Test/')
@@ -67,12 +69,12 @@ def link_file(src_pattern, dst):
67
69
  # link_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test')
68
70
  param {*} src_pattern # 源文件或目录
69
71
  param {*} dst # 目标文件或目录
70
- '''
72
+ """
71
73
  src_pattern = str(src_pattern)
72
74
  # 使用glob.glob来处理可能包含通配符的src
73
75
  src_files = glob.glob(src_pattern)
74
76
  if not src_files:
75
- raise FileNotFoundError('文件不存在: {}'.format(src_pattern))
77
+ raise FileNotFoundError("文件不存在: {}".format(src_pattern))
76
78
 
77
79
  # 判断dst是路径还是包含文件名的路径
78
80
  if os.path.isdir(dst):
@@ -84,7 +86,7 @@ def link_file(src_pattern, dst):
84
86
  if os.path.exists(dst_file):
85
87
  os.remove(dst_file)
86
88
  os.symlink(src_file, dst_file)
87
- print(f'创建符号链接: {src_file} -> {dst_file}')
89
+ print(f"创建符号链接: {src_file} -> {dst_file}")
88
90
  else:
89
91
  # 如果dst包含文件名,则创建链接后重命名
90
92
  dst_dir = os.path.dirname(dst)
@@ -95,23 +97,24 @@ def link_file(src_pattern, dst):
95
97
  if os.path.exists(dst_file):
96
98
  os.remove(dst_file)
97
99
  os.symlink(src_file, dst_file)
98
- print(f'创建符号链接并重命名: {src_file} -> {dst_file}')
100
+ print(f"创建符号链接并重命名: {src_file} -> {dst_file}")
99
101
 
100
102
 
103
+ # ** 复制文件或目录,支持通配符
101
104
  def copy_file(src_pattern, dst):
102
- '''
105
+ """
103
106
  # 描述:复制文件或目录,支持通配符
104
107
  # 使用示例
105
108
  # copy_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test/py.o')
106
109
  # copy_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test')
107
110
  param {*} src_pattern # 源文件或目录
108
111
  param {*} dst # 目标文件或目录
109
- '''
112
+ """
110
113
  src_pattern = str(src_pattern)
111
114
  # 使用glob.glob来处理可能包含通配符的src
112
115
  src_files = glob.glob(src_pattern)
113
116
  if not src_files:
114
- raise FileNotFoundError('文件不存在: {}'.format(src_pattern))
117
+ raise FileNotFoundError("文件不存在: {}".format(src_pattern))
115
118
 
116
119
  # 判断dst是路径还是包含文件名的路径
117
120
  if os.path.isdir(dst):
@@ -129,7 +132,7 @@ def copy_file(src_pattern, dst):
129
132
  shutil.copytree(src_file, dst_file, symlinks=True)
130
133
  else:
131
134
  shutil.copy2(src_file, dst_file)
132
- print(f'复制文件或目录: {src_file} -> {dst_file}')
135
+ print(f"复制文件或目录: {src_file} -> {dst_file}")
133
136
  else:
134
137
  # 如果dst包含文件名,则复制后重命名
135
138
  dst_dir = os.path.dirname(dst)
@@ -146,11 +149,12 @@ def copy_file(src_pattern, dst):
146
149
  shutil.copytree(src_file, dst_file, symlinks=True)
147
150
  else:
148
151
  shutil.copy2(src_file, dst_file)
149
- print(f'复制文件或目录并重命名: {src_file} -> {dst_file}')
152
+ print(f"复制文件或目录并重命名: {src_file} -> {dst_file}")
150
153
 
151
154
 
155
+ # ** 重命名文件,支持通配符
152
156
  def rename_file(directory, old_str, new_str):
153
- '''
157
+ """
154
158
  # 描述:重命名目录下的文件,支持通配符
155
159
  # 使用示例
156
160
  directory_path = r"E:\windfarm\CROCO_FILES"
@@ -160,7 +164,7 @@ def rename_file(directory, old_str, new_str):
160
164
  param {*} directory # 目录
161
165
  param {*} old_str # 要替换的字符串
162
166
  param {*} new_str # 新字符串
163
- '''
167
+ """
164
168
  # 获取目录下的所有文件
165
169
  files = os.listdir(directory)
166
170
 
@@ -187,7 +191,7 @@ def rename_file(directory, old_str, new_str):
187
191
 
188
192
  # ** 创建子文件夹(可选清空)
189
193
  def make_folder(rootpath: str, folder_name: str, clear=0) -> str:
190
- '''
194
+ """
191
195
  # 描述:创建子文件夹(可选清空)
192
196
  # 使用示例
193
197
  rootpath = r'E:\Data\2024\09\17'
@@ -195,23 +199,22 @@ def make_folder(rootpath: str, folder_name: str, clear=0) -> str:
195
199
  newpath = make_folder(rootpath, folder_name, clear=1)
196
200
  param {*} rootpath # 根目录
197
201
  param {*} folder_name # 文件夹名称
198
- '''
202
+ """
199
203
  folder_path = os.path.join(str(rootpath), str(folder_name))
200
204
  if clear:
201
205
  shutil.rmtree(folder_path, ignore_errors=True)
202
206
  os.makedirs(folder_path, exist_ok=True)
203
207
  return folder_path
204
208
 
205
- # ** 清空文件夹
206
-
207
209
 
210
+ # ** 清空文件夹
208
211
  def clear_folder(folder_path):
209
- '''
212
+ """
210
213
  # 描述:清空文件夹
211
214
  # 使用示例
212
215
  clear_folder(r'E:\Data\2024\09\17\var1')
213
216
  param {*} folder_path # 文件夹路径
214
- '''
217
+ """
215
218
  folder_path = str(folder_path)
216
219
  if os.path.exists(folder_path):
217
220
  try:
@@ -223,21 +226,21 @@ def clear_folder(folder_path):
223
226
  os.unlink(file_path) # 删除文件或链接
224
227
  elif os.path.isdir(file_path):
225
228
  shutil.rmtree(file_path) # 删除子文件夹
226
- print(f'成功清空文件夹: {folder_path}')
229
+ print(f"成功清空文件夹: {folder_path}")
227
230
  except Exception as e:
228
- print(f'清空文件夹失败: {folder_path}')
231
+ print(f"清空文件夹失败: {folder_path}")
229
232
  print(e)
230
233
 
231
234
 
232
235
  # ** 清理空文件夹
233
236
  def remove_empty_folders(path, print_info=1):
234
- '''
237
+ """
235
238
  # 描述:清理空文件夹
236
239
  # 使用示例
237
240
  remove_empty_folders(r'E:\Data\2024\09\17', print_info=1)
238
241
  param {*} path # 文件夹路径
239
242
  param {*} print_info # 是否打印信息
240
- '''
243
+ """
241
244
  path = str(path)
242
245
  # 遍历当前目录下的所有文件夹和文件
243
246
  for root, dirs, files in os.walk(path, topdown=False):
@@ -263,14 +266,14 @@ def remove_empty_folders(path, print_info=1):
263
266
 
264
267
  # ** 删除相关文件,可使用通配符
265
268
  def remove(pattern):
266
- '''
269
+ """
267
270
  # 描述:删除相关文件,可使用通配符
268
271
  remove(r'E:\Code\Python\Model\WRF\Radar2\bzip2-radar-0*')
269
272
  # or
270
273
  os.chdir(r'E:\Code\Python\Model\WRF\Radar2')
271
274
  remove('bzip2-radar-0*')
272
275
  param {*} pattern # 文件路径或通配符
273
- '''
276
+ """
274
277
  # 使用glob.glob来获取所有匹配的文件
275
278
  # 可以使用通配符*来匹配所有文件
276
279
  pattern = str(pattern)
@@ -279,16 +282,17 @@ def remove(pattern):
279
282
  if os.path.exists(file_path):
280
283
  try:
281
284
  shutil.rmtree(file_path)
282
- print(f'成功删除文件: {file_path}')
285
+ print(f"成功删除文件: {file_path}")
283
286
  except Exception as e:
284
- print(f'删除文件失败: {file_path}')
287
+ print(f"删除文件失败: {file_path}")
285
288
  print(e)
286
289
  else:
287
- print(f'文件不存在: {file_path}')
290
+ print(f"文件不存在: {file_path}")
288
291
 
289
292
 
290
- def file_size(file_path, unit='KB'):
291
- '''
293
+ # ** 获取文件大小
294
+ def file_size(file_path, unit="KB"):
295
+ """
292
296
  Description: 获取文件大小
293
297
 
294
298
  Args:
@@ -297,7 +301,7 @@ def file_size(file_path, unit='KB'):
297
301
 
298
302
  Returns:
299
303
  文件大小(单位:PB、TB、GB、MB、KB)
300
- '''
304
+ """
301
305
  # 检查文件是否存在
302
306
  if not os.path.exists(file_path):
303
307
  return "文件不存在"
@@ -306,13 +310,7 @@ def file_size(file_path, unit='KB'):
306
310
  file_size = os.path.getsize(file_path)
307
311
 
308
312
  # 单位转换字典
309
- unit_dict = {
310
- 'PB': 1024**5,
311
- 'TB': 1024**4,
312
- 'GB': 1024**3,
313
- 'MB': 1024**2,
314
- 'KB': 1024
315
- }
313
+ unit_dict = {"PB": 1024**5, "TB": 1024**4, "GB": 1024**3, "MB": 1024**2, "KB": 1024}
316
314
 
317
315
  # 检查传入的单位是否合法
318
316
  if unit not in unit_dict:
@@ -324,9 +322,22 @@ def file_size(file_path, unit='KB'):
324
322
  return converted_size
325
323
 
326
324
 
327
- if __name__ == '__main__':
325
+ # ** 计算文件夹下指定相关文件的平均大小
326
+ def mean_size(parent_path,fname):
327
+ flist = find_file(parent_path, fname)
328
+ if flist:
329
+ size_list = [file_size(f) for f in flist if file_size(f) != 0]
330
+ if size_list:
331
+ min_size, max_size = min(size_list), max(size_list)
332
+ mean_size = sum(size_list) / len(size_list)
333
+ else:
334
+ mean_size, min_size, max_size = 0, 0, 0
335
+ return mean_size, min_size, max_size
336
+
337
+
338
+ if __name__ == "__main__":
328
339
  # newpath = make_folder('D:/Data/2024/09/17/', 'var1', clear=1)
329
340
  # print(newpath)
330
341
  pass
331
342
 
332
- remove(r'I:\Delete\test\*')
343
+ remove(r"I:\Delete\test\*")
oafuncs/oa_nc.py CHANGED
@@ -19,7 +19,7 @@ import netCDF4 as nc
19
19
  import numpy as np
20
20
  import xarray as xr
21
21
 
22
- __all__ = ["get_var", "extract5nc", "write2nc", "merge5nc", "modify_var_value", "modify_var_attr", "rename_var_or_dim", "check_ncfile"]
22
+ __all__ = ["get_var", "extract5nc", "write2nc", "merge5nc", "modify_var_value", "modify_var_attr", "rename_var_or_dim", "check_ncfile", "convert_longitude", "nc_isel"]
23
23
 
24
24
 
25
25
  def get_var(file, *vars):
@@ -38,7 +38,7 @@ def get_var(file, *vars):
38
38
  return datas
39
39
 
40
40
 
41
- def extract5nc(file, varname):
41
+ def extract5nc(file, varname, only_value=True):
42
42
  """
43
43
  描述:
44
44
  1、提取nc文件中的变量
@@ -47,16 +47,22 @@ def extract5nc(file, varname):
47
47
  参数:
48
48
  file: 文件路径
49
49
  varname: 变量名
50
+ only_value: 变量和维度是否只保留数值
50
51
  example: data, dimdict = extract5nc(file_ecm, 'h')
51
52
  """
52
53
  ds = xr.open_dataset(file)
53
54
  vardata = ds[varname]
55
+ ds.close()
54
56
  dims = vardata.dims
55
57
  dimdict = {}
56
58
  for dim in dims:
57
- dimdict[dim] = ds[dim].values
58
- ds.close()
59
- return np.array(vardata), dimdict
59
+ if only_value:
60
+ dimdict[dim] = vardata[dim].values
61
+ else:
62
+ dimdict[dim] = ds[dim]
63
+ if only_value:
64
+ vardata = np.array(vardata)
65
+ return vardata, dimdict
60
66
 
61
67
 
62
68
  def _numpy_to_nc_type(numpy_type):
@@ -73,76 +79,100 @@ def _numpy_to_nc_type(numpy_type):
73
79
  "uint32": "u4",
74
80
  "uint64": "u8",
75
81
  }
76
- return numpy_to_nc.get(str(numpy_type), "f4") # 默认使用 'float32'
82
+ # 确保传入的是字符串类型,如果不是,则转换为字符串
83
+ numpy_type_str = str(numpy_type) if not isinstance(numpy_type, str) else numpy_type
84
+ return numpy_to_nc.get(numpy_type_str, "f4") # 默认使用 'float32'
85
+
86
+
87
+ def _calculate_scale_and_offset(data, n=16):
88
+ if not isinstance(data, np.ndarray):
89
+ raise ValueError("Input data must be a NumPy array.")
77
90
 
91
+ # 使用 nan_to_num 来避免 NaN 值对 min 和 max 的影响
92
+ data_min = np.nanmin(data)
93
+ data_max = np.nanmax(data)
78
94
 
79
- def write2nc(file, data, varname, coords, mode):
95
+ if np.isnan(data_min) or np.isnan(data_max):
96
+ raise ValueError("Input data contains NaN values, which are not allowed.")
97
+
98
+ scale_factor = (data_max - data_min) / (2**n - 1)
99
+ add_offset = data_min + 2 ** (n - 1) * scale_factor
100
+
101
+ return scale_factor, add_offset
102
+
103
+
104
+ def write2nc(file, data, varname=None, coords=None, mode="w", scale_offset_switch=True, compile_switch=True):
80
105
  """
81
106
  description: 写入数据到nc文件
107
+
82
108
  参数:
83
109
  file: 文件路径
84
110
  data: 数据
85
111
  varname: 变量名
86
112
  coords: 坐标,字典,键为维度名称,值为坐标数据
87
113
  mode: 写入模式,'w'为写入,'a'为追加
114
+ scale_offset_switch: 是否使用scale_factor和add_offset,默认为True
115
+ compile_switch: 是否使用压缩参数,默认为True
116
+
88
117
  example: write2nc(r'test.nc', data, 'data', {'time': np.linspace(0, 120, 100), 'lev': np.linspace(0, 120, 50)}, 'a')
89
118
  """
90
- # 判断mode是写入还是追加
91
- if mode == "w":
92
- if os.path.exists(file):
93
- os.remove(file)
94
- print("Warning: File already exists. Deleting it.")
95
- elif mode == "a":
96
- if not os.path.exists(file):
97
- print("Warning: File doesn't exist. Creating a new file.")
98
- mode = "w"
119
+ # 设置压缩参数
120
+ kwargs = {"zlib": True, "complevel": 4} if compile_switch else {}
121
+
122
+ # 检查文件存在性并根据模式决定操作
123
+ if mode == "w" and os.path.exists(file):
124
+ os.remove(file)
125
+ elif mode == "a" and not os.path.exists(file):
126
+ mode = "w"
99
127
 
100
128
  # 打开 NetCDF 文件
101
129
  with nc.Dataset(file, mode, format="NETCDF4") as ncfile:
102
- # 处理坐标
130
+ # 如果 data 是 DataArray 并且没有提供 varname 和 coords
131
+ if varname is None and coords is None and isinstance(data, xr.DataArray):
132
+ data.to_netcdf(file, mode=mode)
133
+ return
134
+
135
+ # 添加坐标
103
136
  for dim, coord_data in coords.items():
104
- add_coords = True
105
- # 判断坐标是否存在,若存在,则替换/报错
106
- if ncfile.dimensions:
107
- # 返回字典,字典、列表、元组若为空,都表示False
108
- if dim in ncfile.dimensions:
109
- # del nc.dimensions[dim]
110
- if len(coord_data) != len(ncfile.dimensions[dim]):
111
- raise ValueError("Length of coordinate does not match the dimension length.")
112
- else:
113
- add_coords = False
114
- print(f"Warning: Coordinate '{dim}' already exists. Replacing it.")
115
- ncfile.variables[dim][:] = np.array(coord_data)
116
- if add_coords:
117
- # 创建新坐标
137
+ if dim in ncfile.dimensions:
138
+ if len(coord_data) != len(ncfile.dimensions[dim]):
139
+ raise ValueError(f"Length of coordinate '{dim}' does not match the dimension length.")
140
+ else:
141
+ ncfile.variables[dim][:] = np.array(coord_data)
142
+ else:
118
143
  ncfile.createDimension(dim, len(coord_data))
119
- ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,))
120
- ncfile.variables[dim][:] = np.array(coord_data)
144
+ var = ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,), **kwargs)
145
+ var[:] = np.array(coord_data)
121
146
 
122
- # 判断变量是否存在,若存在,则删除原变量
123
- add_var = True
147
+ # 如果坐标数据有属性,则添加到 NetCDF 变量
148
+ if isinstance(coord_data, xr.DataArray) and coord_data.attrs:
149
+ for attr_name, attr_value in coord_data.attrs.items():
150
+ var.setncattr(attr_name, attr_value)
151
+
152
+ # 添加或更新变量
124
153
  if varname in ncfile.variables:
125
- print(f"Warning: Variable '{varname}' already exists.")
126
154
  if data.shape != ncfile.variables[varname].shape:
127
- raise ValueError("Shape of data does not match the variable shape.")
155
+ raise ValueError(f"Shape of data does not match the variable shape for '{varname}'.")
156
+ ncfile.variables[varname][:] = np.array(data)
157
+ else:
158
+ # 创建变量
159
+ dim_names = tuple(coords.keys())
160
+ if scale_offset_switch:
161
+ scale_factor, add_offset = _calculate_scale_and_offset(np.array(data))
162
+ dtype = "i2"
163
+ var = ncfile.createVariable(varname, dtype, dim_names, fill_value=-32767, **kwargs)
164
+ var.setncattr("scale_factor", scale_factor)
165
+ var.setncattr("add_offset", add_offset)
128
166
  else:
129
- # 写入数据
130
- ncfile.variables[varname][:] = data
131
- add_var = False
132
- print(f"Warning: Variable '{varname}' already exists. Replacing it.")
133
-
134
- if add_var:
135
- # 创建变量及其维度
136
- dim_names = tuple(coords.keys()) # 使用coords传入的维度名称
137
- ncfile.createVariable(varname, _numpy_to_nc_type(data.dtype), dim_names)
138
- # ncfile.createVariable('data', 'f4', ('time','lev'))
139
-
140
- # 写入数据
141
- ncfile.variables[varname][:] = data
167
+ dtype = _numpy_to_nc_type(data.dtype)
168
+ var = ncfile.createVariable(varname, dtype, dim_names, **kwargs)
169
+ var[:] = np.array(data)
142
170
 
143
- # 判断维度是否匹配
144
- if len(data.shape) != len(coords):
145
- raise ValueError("Number of dimensions does not match the data shape.")
171
+ # 添加属性
172
+ if isinstance(data, xr.DataArray) and data.attrs:
173
+ for key, value in data.attrs.items():
174
+ if key not in ["scale_factor", "add_offset", "_FillValue", "missing_value"] or not scale_offset_switch:
175
+ var.setncattr(key, value)
146
176
 
147
177
 
148
178
  def merge5nc(file_list, var_name=None, dim_name=None, target_filename=None):
@@ -162,6 +192,9 @@ def merge5nc(file_list, var_name=None, dim_name=None, target_filename=None):
162
192
  merge5nc(file_list, var_name=['data1', 'data2'], dim_name='time', target_filename='merged.nc')
163
193
  merge5nc(file_list, var_name=None, dim_name='time', target_filename='merged.nc')
164
194
  """
195
+ if isinstance(file_list, str):
196
+ file_list = [file_list]
197
+
165
198
  # 初始化变量名列表
166
199
  var_names = None
167
200
 
@@ -330,6 +363,51 @@ def check_ncfile(ncfile, if_delete=False):
330
363
  return False
331
364
 
332
365
 
366
+ def convert_longitude(ds, lon_name="longitude", convert=180):
367
+ """
368
+ 将经度数组转换为指定的范围。
369
+
370
+ 参数:
371
+ ds (xarray.Dataset): 包含经度数据的xarray数据集。
372
+ lon_name (str): 经度变量的名称,默认为"longitude"。
373
+ convert (int): 转换目标范围,可以是180或360,默认为180。
374
+
375
+ 返回值:
376
+ xarray.Dataset: 经度转换后的xarray数据集。
377
+ """
378
+ to_which = int(convert)
379
+ if to_which not in [180, 360]:
380
+ raise ValueError("to_which must be '180' or '360'")
381
+
382
+ if to_which == 180:
383
+ ds = ds.assign_coords({lon_name: (ds[lon_name] + 180) % 360 - 180})
384
+ elif to_which == 360:
385
+ ds = ds.assign_coords({lon_name: (ds[lon_name] + 360) % 360})
386
+
387
+ return ds.sortby(lon_name)
388
+
389
+
390
+ def nc_isel(ncfile, dim_name, slice_list):
391
+ """
392
+ Description: Choose the data by the index of the dimension
393
+
394
+ Parameters:
395
+ ncfile: str, the path of the netCDF file
396
+ dim_name: str, the name of the dimension
397
+ slice_list: list, the index of the dimension
398
+
399
+ slice_list example: slice_list = [[y*12+m for m in range(11,14)] for y in range(84)]
400
+ or
401
+ slice_list = [y * 12 + m for y in range(84) for m in range(11, 14)]
402
+ """
403
+ ds = xr.open_dataset(ncfile)
404
+ slice_list = np.array(slice_list).flatten()
405
+ slice_list = [int(i) for i in slice_list]
406
+ ds_new = ds.isel(**{dim_name: slice_list})
407
+ ds.close()
408
+ return ds_new
409
+
410
+
333
411
  if __name__ == "__main__":
334
412
  data = np.random.rand(100, 50)
335
413
  write2nc(r"test.nc", data, "data", {"time": np.linspace(0, 120, 100), "lev": np.linspace(0, 120, 50)}, "a")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oafuncs
3
- Version: 0.0.80
3
+ Version: 0.0.82
4
4
  Summary: My short description for my project.
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -13,7 +13,6 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
16
  Classifier: Programming Language :: Python :: Implementation :: CPython
18
17
  Classifier: Programming Language :: Python :: Implementation :: PyPy
19
18
  Requires-Python: >=3.9.0
@@ -0,0 +1,24 @@
1
+ oafuncs/__init__.py,sha256=glcIlhQ9xSK4WtL58dq7Od2S3JPqsuEyhUQ-VWO8hOc,1426
2
+ oafuncs/oa_cmap.py,sha256=N-jt5CjRTXIjur6QAh90L8hAr9n3D-1HRSkKT98hUx8,6449
3
+ oafuncs/oa_data.py,sha256=DUKc--EyQfIcxAwy2Rv_oDMuE5uw9CVrt0bMAfVog98,12482
4
+ oafuncs/oa_draw.py,sha256=ccwFS3Y4DmzLLcqSrX-Ee-vCdWd78D1UcaSdTWoDC9Q,20950
5
+ oafuncs/oa_file.py,sha256=FkEYm-JfbQoJZHoY28El412k19JiQeNhO67ybcOhORg,12222
6
+ oafuncs/oa_help.py,sha256=ppNktmtNzs15R20MD1bM7yImlTQ_ngMwvoIglePOKXA,1000
7
+ oafuncs/oa_nc.py,sha256=Gj0xLfqgQI3hNnr9SED1WFj2JU4GsJj2on_8EVdSk20,15595
8
+ oafuncs/oa_python.py,sha256=XPTP3o7zTFzfJR_YhsKfQksa3bSYwXsne9YxlJplCEA,3994
9
+ oafuncs/oa_down/User_Agent-list.txt,sha256=pazxSip8_lphEBOPHG902zmIBUg8sBKXgmqp_g6j_E4,661062
10
+ oafuncs/oa_down/__init__.py,sha256=nY5X7gM1jw7DJxyooR2UJSq4difkw-flz2Ucr_OuDbA,540
11
+ oafuncs/oa_down/hycom_3hourly.py,sha256=7YLtN1SqugcGQ1ZdUznUDspdJGZONphBq6LoxihIdyw,62938
12
+ oafuncs/oa_down/literature.py,sha256=dT3-7-beEzQ9mTP8LNV9Gf3q5Z1Pqqjc6FOS010HZeQ,17833
13
+ oafuncs/oa_down/test_ua.py,sha256=0IQq3NjqfNr7KkyjS_U-a4mYu-r-E7gzawwo4IfEa6Y,10851
14
+ oafuncs/oa_sign/__init__.py,sha256=QKqTFrJDFK40C5uvk48GlRRbGFzO40rgkYwu6dYxatM,563
15
+ oafuncs/oa_sign/meteorological.py,sha256=mLbupsZSq427HTfVbZMvIlFzDHwSzQAbK3X19o8anFY,6525
16
+ oafuncs/oa_sign/ocean.py,sha256=xrW-rWD7xBWsB5PuCyEwQ1Q_RDKq2KCLz-LOONHgldU,5932
17
+ oafuncs/oa_sign/scientific.py,sha256=a4JxOBgm9vzNZKpJ_GQIQf7cokkraV5nh23HGbmTYKw,5064
18
+ oafuncs/oa_tool/__init__.py,sha256=IKOlqpWlb4cMDCtq2VKR_RTxQHDNqR_vfqqsOsp_lKQ,466
19
+ oafuncs/oa_tool/email.py,sha256=4lJxV_KUzhxgLYfVwYTqp0qxRugD7fvsZkXDe5WkUKo,3052
20
+ oafuncs-0.0.82.dist-info/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
21
+ oafuncs-0.0.82.dist-info/METADATA,sha256=wKkzaYKFDa8_O_EhelPy0RIiD0H93MHhZlEMNI2V2QE,22429
22
+ oafuncs-0.0.82.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109
23
+ oafuncs-0.0.82.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
24
+ oafuncs-0.0.82.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ oafuncs