oafuncs 0.0.81__py2.py3-none-any.whl → 0.0.83__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 (44) hide show
  1. oafuncs/data_store/OAFuncs.png +0 -0
  2. oafuncs/oa_cmap.py +1 -0
  3. oafuncs/oa_data.py +107 -28
  4. oafuncs/oa_down/__init__.py +5 -4
  5. oafuncs/oa_down/hycom_3hourly.py +152 -35
  6. oafuncs/oa_down/user_agent.py +34 -0
  7. oafuncs/oa_draw.py +165 -103
  8. oafuncs/oa_file.py +66 -53
  9. oafuncs/oa_help.py +19 -16
  10. oafuncs/oa_nc.py +82 -114
  11. oafuncs-0.0.83.dist-info/METADATA +91 -0
  12. oafuncs-0.0.83.dist-info/RECORD +26 -0
  13. oafuncs/oa_down/test.py +0 -151
  14. oafuncs/oa_s/__init__.py +0 -23
  15. oafuncs/oa_s/oa_cmap.py +0 -163
  16. oafuncs/oa_s/oa_data.py +0 -187
  17. oafuncs/oa_s/oa_draw.py +0 -451
  18. oafuncs/oa_s/oa_file.py +0 -332
  19. oafuncs/oa_s/oa_help.py +0 -39
  20. oafuncs/oa_s/oa_nc.py +0 -410
  21. oafuncs/oa_s/oa_python.py +0 -107
  22. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/__init__.py" +0 -26
  23. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_cmap.py" +0 -163
  24. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_data.py" +0 -187
  25. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/__init__.py" +0 -20
  26. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/hycom_3hourly.py" +0 -1176
  27. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/literature.py" +0 -332
  28. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/test_ua.py" +0 -151
  29. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_draw.py" +0 -451
  30. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_file.py" +0 -332
  31. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_help.py" +0 -39
  32. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_nc.py" +0 -410
  33. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_python.py" +0 -107
  34. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/__init__.py" +0 -21
  35. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/meteorological.py" +0 -168
  36. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/ocean.py" +0 -158
  37. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/scientific.py" +0 -139
  38. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/__init__.py" +0 -18
  39. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/email.py" +0 -114
  40. oafuncs-0.0.81.dist-info/METADATA +0 -918
  41. oafuncs-0.0.81.dist-info/RECORD +0 -51
  42. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/LICENSE.txt +0 -0
  43. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/WHEEL +0 -0
  44. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/top_level.txt +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_folder", "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
 
@@ -186,8 +190,8 @@ def rename_file(directory, old_str, new_str):
186
190
 
187
191
 
188
192
  # ** 创建子文件夹(可选清空)
189
- def make_folder(rootpath: str, folder_name: str, clear=0) -> str:
190
- '''
193
+ def make_folder(rootpath=None, folder_name=None, clear=0) -> str:
194
+ """
191
195
  # 描述:创建子文件夹(可选清空)
192
196
  # 使用示例
193
197
  rootpath = r'E:\Data\2024\09\17'
@@ -195,23 +199,24 @@ 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
+ """
203
+ if rootpath is None:
204
+ rootpath = os.getcwd()
199
205
  folder_path = os.path.join(str(rootpath), str(folder_name))
200
206
  if clear:
201
207
  shutil.rmtree(folder_path, ignore_errors=True)
202
208
  os.makedirs(folder_path, exist_ok=True)
203
209
  return folder_path
204
210
 
205
- # ** 清空文件夹
206
-
207
211
 
212
+ # ** 清空文件夹
208
213
  def clear_folder(folder_path):
209
- '''
214
+ """
210
215
  # 描述:清空文件夹
211
216
  # 使用示例
212
217
  clear_folder(r'E:\Data\2024\09\17\var1')
213
218
  param {*} folder_path # 文件夹路径
214
- '''
219
+ """
215
220
  folder_path = str(folder_path)
216
221
  if os.path.exists(folder_path):
217
222
  try:
@@ -223,21 +228,21 @@ def clear_folder(folder_path):
223
228
  os.unlink(file_path) # 删除文件或链接
224
229
  elif os.path.isdir(file_path):
225
230
  shutil.rmtree(file_path) # 删除子文件夹
226
- print(f'成功清空文件夹: {folder_path}')
231
+ print(f"成功清空文件夹: {folder_path}")
227
232
  except Exception as e:
228
- print(f'清空文件夹失败: {folder_path}')
233
+ print(f"清空文件夹失败: {folder_path}")
229
234
  print(e)
230
235
 
231
236
 
232
237
  # ** 清理空文件夹
233
- def remove_empty_folders(path, print_info=1):
234
- '''
238
+ def remove_empty_folder(path, print_info=1):
239
+ """
235
240
  # 描述:清理空文件夹
236
241
  # 使用示例
237
- remove_empty_folders(r'E:\Data\2024\09\17', print_info=1)
242
+ remove_empty_folder(r'E:\Data\2024\09\17', print_info=1)
238
243
  param {*} path # 文件夹路径
239
244
  param {*} print_info # 是否打印信息
240
- '''
245
+ """
241
246
  path = str(path)
242
247
  # 遍历当前目录下的所有文件夹和文件
243
248
  for root, dirs, files in os.walk(path, topdown=False):
@@ -263,14 +268,14 @@ def remove_empty_folders(path, print_info=1):
263
268
 
264
269
  # ** 删除相关文件,可使用通配符
265
270
  def remove(pattern):
266
- '''
271
+ """
267
272
  # 描述:删除相关文件,可使用通配符
268
273
  remove(r'E:\Code\Python\Model\WRF\Radar2\bzip2-radar-0*')
269
274
  # or
270
275
  os.chdir(r'E:\Code\Python\Model\WRF\Radar2')
271
276
  remove('bzip2-radar-0*')
272
277
  param {*} pattern # 文件路径或通配符
273
- '''
278
+ """
274
279
  # 使用glob.glob来获取所有匹配的文件
275
280
  # 可以使用通配符*来匹配所有文件
276
281
  pattern = str(pattern)
@@ -279,16 +284,17 @@ def remove(pattern):
279
284
  if os.path.exists(file_path):
280
285
  try:
281
286
  shutil.rmtree(file_path)
282
- print(f'成功删除文件: {file_path}')
287
+ print(f"成功删除文件: {file_path}")
283
288
  except Exception as e:
284
- print(f'删除文件失败: {file_path}')
289
+ print(f"删除文件失败: {file_path}")
285
290
  print(e)
286
291
  else:
287
- print(f'文件不存在: {file_path}')
292
+ print(f"文件不存在: {file_path}")
288
293
 
289
294
 
290
- def file_size(file_path, unit='KB'):
291
- '''
295
+ # ** 获取文件大小
296
+ def file_size(file_path, unit="KB"):
297
+ """
292
298
  Description: 获取文件大小
293
299
 
294
300
  Args:
@@ -297,7 +303,7 @@ def file_size(file_path, unit='KB'):
297
303
 
298
304
  Returns:
299
305
  文件大小(单位:PB、TB、GB、MB、KB)
300
- '''
306
+ """
301
307
  # 检查文件是否存在
302
308
  if not os.path.exists(file_path):
303
309
  return "文件不存在"
@@ -306,13 +312,7 @@ def file_size(file_path, unit='KB'):
306
312
  file_size = os.path.getsize(file_path)
307
313
 
308
314
  # 单位转换字典
309
- unit_dict = {
310
- 'PB': 1024**5,
311
- 'TB': 1024**4,
312
- 'GB': 1024**3,
313
- 'MB': 1024**2,
314
- 'KB': 1024
315
- }
315
+ unit_dict = {"PB": 1024**5, "TB": 1024**4, "GB": 1024**3, "MB": 1024**2, "KB": 1024}
316
316
 
317
317
  # 检查传入的单位是否合法
318
318
  if unit not in unit_dict:
@@ -324,9 +324,22 @@ def file_size(file_path, unit='KB'):
324
324
  return converted_size
325
325
 
326
326
 
327
- if __name__ == '__main__':
327
+ # ** 计算文件夹下指定相关文件的平均大小
328
+ def mean_size(parent_path,fname):
329
+ flist = find_file(parent_path, fname)
330
+ if flist:
331
+ size_list = [file_size(f) for f in flist if file_size(f) != 0]
332
+ if size_list:
333
+ min_size, max_size = min(size_list), max(size_list)
334
+ mean_size = sum(size_list) / len(size_list)
335
+ else:
336
+ mean_size, min_size, max_size = 0, 0, 0
337
+ return mean_size, min_size, max_size
338
+
339
+
340
+ if __name__ == "__main__":
328
341
  # newpath = make_folder('D:/Data/2024/09/17/', 'var1', clear=1)
329
342
  # print(newpath)
330
343
  pass
331
344
 
332
- remove(r'I:\Delete\test\*')
345
+ remove(r"I:\Delete\test\*")
oafuncs/oa_help.py CHANGED
@@ -1,39 +1,42 @@
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-10-06 19:25:29
6
6
  LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2024-11-21 12:59:18
7
+ LastEditTime: 2024-12-26 10:43:40
8
8
  FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_help.py
9
- Description:
9
+ Description:
10
10
  EditPlatform: vscode
11
11
  ComputerInfo: XPS 15 9510
12
12
  SystemInfo: Windows 11
13
- Python Version: 3.11
14
- '''
13
+ Python Version: 3.12
14
+ """
15
15
 
16
16
  import oafuncs
17
17
 
18
+ __all__ = ["query", "use"]
19
+
20
+
18
21
  def query():
19
- '''
20
- description: 查看OAFuncs模块的函数列表
22
+ """
23
+ description: 查看oafuncs模块的函数列表
21
24
  example: query()
22
- '''
25
+ """
23
26
  funcs = [func for func in dir(oafuncs) if callable(getattr(oafuncs, func))]
24
- print('函数数量:')
27
+ print("函数数量:")
25
28
  print(len(funcs))
26
- print('函数列表:')
29
+ print("函数列表:")
27
30
  print(funcs)
28
31
 
29
32
 
30
- def use(func='get_var'):
31
- '''
33
+ def use(func="get_var"):
34
+ """
32
35
  description: 查看函数的模块全路径和函数提示
33
36
  param {func} : 函数名
34
37
  example: use('get_var')
35
- '''
36
- print('模块全路径:')
37
- print(getattr(oafuncs, func).__module__+'.'+func)
38
- print('函数提示:')
38
+ """
39
+ print("模块全路径:")
40
+ print(getattr(oafuncs, func).__module__ + "." + func)
41
+ print("函数提示:")
39
42
  print(getattr(oafuncs, func).__doc__)
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", "longitude_change", "nc_isel"]
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):
@@ -79,21 +79,32 @@ def _numpy_to_nc_type(numpy_type):
79
79
  "uint32": "u4",
80
80
  "uint64": "u8",
81
81
  }
82
- 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'
83
85
 
84
86
 
85
87
  def _calculate_scale_and_offset(data, n=16):
86
- data_min, data_max = np.nanmin(data), np.nanmax(data)
87
- scale_factor = (data_max - data_min) / (2 ** n - 1)
88
+ if not isinstance(data, np.ndarray):
89
+ raise ValueError("Input data must be a NumPy array.")
90
+
91
+ # 使用 nan_to_num 来避免 NaN 值对 min 和 max 的影响
92
+ data_min = np.nanmin(data)
93
+ data_max = np.nanmax(data)
94
+
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)
88
99
  add_offset = data_min + 2 ** (n - 1) * scale_factor
89
- # S = Q * scale_factor + add_offset
100
+
90
101
  return scale_factor, add_offset
91
102
 
92
103
 
93
- def write2nc(file, data, varname=None, coords=None, mode='w', scale_offset_switch=True, compile_switch=True):
104
+ def write2nc(file, data, varname=None, coords=None, mode="w", scale_offset_switch=True, compile_switch=True):
94
105
  """
95
106
  description: 写入数据到nc文件
96
-
107
+
97
108
  参数:
98
109
  file: 文件路径
99
110
  data: 数据
@@ -102,115 +113,66 @@ def write2nc(file, data, varname=None, coords=None, mode='w', scale_offset_switc
102
113
  mode: 写入模式,'w'为写入,'a'为追加
103
114
  scale_offset_switch: 是否使用scale_factor和add_offset,默认为True
104
115
  compile_switch: 是否使用压缩参数,默认为True
105
-
116
+
106
117
  example: write2nc(r'test.nc', data, 'data', {'time': np.linspace(0, 120, 100), 'lev': np.linspace(0, 120, 50)}, 'a')
107
118
  """
108
- # 判断mode是写入还是追加
109
- if mode == "w":
110
- if os.path.exists(file):
111
- os.remove(file)
112
- print("Warning: File already exists. Deleting it.")
113
- elif mode == "a":
114
- if not os.path.exists(file):
115
- print("Warning: File doesn't exist. Creating a new file.")
116
- mode = "w"
117
-
118
- complete = False
119
- if varname is None and coords is None:
120
- try:
121
- data.to_netcdf(file)
122
- complete = True
123
- # 不能在这里return
124
- except AttributeError:
125
- raise ValueError("If varname and coords are None, data must be a DataArray.")
126
-
127
- if complete:
128
- return
129
-
130
- kwargs = {'zlib': True, 'complevel': 4} # 压缩参数
131
- # kwargs = {"compression": 'zlib', "complevel": 4} # 压缩参数
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"
132
127
 
133
128
  # 打开 NetCDF 文件
134
129
  with nc.Dataset(file, mode, format="NETCDF4") as ncfile:
135
- # 处理坐标
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
+ # 添加坐标
136
136
  for dim, coord_data in coords.items():
137
- add_coords = True
138
- # 判断坐标是否存在,若存在,则替换/报错
139
- if ncfile.dimensions:
140
- # 返回字典,字典、列表、元组若为空,都表示False
141
- if dim in ncfile.dimensions:
142
- # del nc.dimensions[dim]
143
- if len(coord_data) != len(ncfile.dimensions[dim]):
144
- raise ValueError("Length of coordinate does not match the dimension length.")
145
- else:
146
- add_coords = False
147
- print(f"Warning: Coordinate '{dim}' already exists. Replacing it.")
148
- ncfile.variables[dim][:] = np.array(coord_data)
149
- if add_coords:
150
- # 创建新坐标
151
- ncfile.createDimension(dim, len(coord_data))
152
- if compile_switch:
153
- ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,), **kwargs)
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.")
154
140
  else:
155
- ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,))
156
- ncfile.variables[dim][:] = np.array(coord_data)
157
-
158
- if isinstance(coord_data, xr.DataArray):
159
- current_var = ncfile.variables[dim]
160
- if coord_data.attrs:
161
- for key, value in coord_data.attrs.items():
162
- current_var.setncattr(key, value)
163
-
164
- # 判断变量是否存在,若存在,则删除原变量
165
- add_var = True
141
+ ncfile.variables[dim][:] = np.array(coord_data)
142
+ else:
143
+ ncfile.createDimension(dim, len(coord_data))
144
+ var = ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,), **kwargs)
145
+ var[:] = np.array(coord_data)
146
+
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
+ # 添加或更新变量
166
153
  if varname in ncfile.variables:
167
- print(f"Warning: Variable '{varname}' already exists.")
168
154
  if data.shape != ncfile.variables[varname].shape:
169
- raise ValueError("Shape of data does not match the variable shape.")
170
- else:
171
- # 写入数据
172
- ncfile.variables[varname][:] = np.array(data)
173
- add_var = False
174
- print(f"Warning: Variable '{varname}' already exists. Replacing it.")
175
-
176
- if add_var:
177
- # 创建变量及其维度
178
- dim_names = tuple(coords.keys()) # 使用coords传入的维度名称
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())
179
160
  if scale_offset_switch:
180
161
  scale_factor, add_offset = _calculate_scale_and_offset(np.array(data))
181
- _FillValue = -32767
182
- missing_value = -32767
183
- dtype = 'i2' # short类型
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)
184
166
  else:
185
167
  dtype = _numpy_to_nc_type(data.dtype)
186
-
187
- if compile_switch:
188
- ncfile.createVariable(varname, dtype, dim_names, **kwargs)
189
- else:
190
- ncfile.createVariable(varname, dtype, dim_names)
191
-
192
- if scale_offset_switch: # 需要在写入数据之前设置scale_factor和add_offset
193
- ncfile.variables[varname].setncattr('scale_factor', scale_factor)
194
- ncfile.variables[varname].setncattr('add_offset', add_offset)
195
- ncfile.variables[varname].setncattr('_FillValue', _FillValue)
196
- ncfile.variables[varname].setncattr('missing_value', missing_value)
197
-
198
- # ncfile.createVariable('data', 'f4', ('time','lev'))
199
-
200
- # 写入数据
201
- ncfile.variables[varname][:] = np.array(data)
168
+ var = ncfile.createVariable(varname, dtype, dim_names, **kwargs)
169
+ var[:] = np.array(data)
202
170
 
203
- # 判断维度是否匹配
204
- if len(data.shape) != len(coords):
205
- raise ValueError("Number of dimensions does not match the data shape.")
206
- # 判断data是否带有属性信息,如果有,写入属性信息
207
- if isinstance(data, xr.DataArray):
208
- current_var = ncfile.variables[varname]
209
- if data.attrs:
171
+ # 添加属性
172
+ if isinstance(data, xr.DataArray) and data.attrs:
210
173
  for key, value in data.attrs.items():
211
- if key in ["scale_factor", "add_offset", "_FillValue", "missing_value"] and scale_offset_switch:
212
- continue
213
- current_var.setncattr(key, value)
174
+ if key not in ["scale_factor", "add_offset", "_FillValue", "missing_value"] or not scale_offset_switch:
175
+ var.setncattr(key, value)
214
176
 
215
177
 
216
178
  def merge5nc(file_list, var_name=None, dim_name=None, target_filename=None):
@@ -230,6 +192,9 @@ def merge5nc(file_list, var_name=None, dim_name=None, target_filename=None):
230
192
  merge5nc(file_list, var_name=['data1', 'data2'], dim_name='time', target_filename='merged.nc')
231
193
  merge5nc(file_list, var_name=None, dim_name='time', target_filename='merged.nc')
232
194
  """
195
+ if isinstance(file_list, str):
196
+ file_list = [file_list]
197
+
233
198
  # 初始化变量名列表
234
199
  var_names = None
235
200
 
@@ -398,25 +363,28 @@ def check_ncfile(ncfile, if_delete=False):
398
363
  return False
399
364
 
400
365
 
401
- def longitude_change(ds, lon_name="longitude", to_which="180"):
366
+ def convert_longitude(ds, lon_name="longitude", convert=180):
402
367
  """
403
- 将经度转换为 -180 到 180 之间
368
+ 将经度数组转换为指定的范围。
404
369
 
405
370
  参数:
406
- lon (numpy.ndarray): 经度数组
371
+ ds (xarray.Dataset): 包含经度数据的xarray数据集。
372
+ lon_name (str): 经度变量的名称,默认为"longitude"。
373
+ convert (int): 转换目标范围,可以是180或360,默认为180。
407
374
 
408
375
  返回值:
409
- numpy.ndarray: 转换后的经度数组
376
+ xarray.Dataset: 经度转换后的xarray数据集。
410
377
  """
411
- # return (lon + 180) % 360 - 180
412
- # ds = ds.assign_coords(longitude=(((ds.longitude + 180) % 360) - 180)).sortby("longitude")
413
- if to_which == "180":
414
- # ds = ds.assign_coords(**{lon_name: (((ds[lon_name] + 180) % 360) - 180)}).sortby(lon_name)
415
- ds = ds.assign_coords(**{lon_name: (ds[lon_name] + 180) % 360 - 180}).sortby(lon_name)
416
- elif to_which == "360":
417
- # -180 to 180 to 0 to 360
418
- ds = ds.assign_coords(**{lon_name: (ds[lon_name] + 360) % 360}).sortby(lon_name)
419
- return ds
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)
420
388
 
421
389
 
422
390
  def nc_isel(ncfile, dim_name, slice_list):