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.
- oafuncs/data_store/OAFuncs.png +0 -0
- oafuncs/oa_cmap.py +1 -0
- oafuncs/oa_data.py +107 -28
- oafuncs/oa_down/__init__.py +5 -4
- oafuncs/oa_down/hycom_3hourly.py +152 -35
- oafuncs/oa_down/user_agent.py +34 -0
- oafuncs/oa_draw.py +165 -103
- oafuncs/oa_file.py +66 -53
- oafuncs/oa_help.py +19 -16
- oafuncs/oa_nc.py +82 -114
- oafuncs-0.0.83.dist-info/METADATA +91 -0
- oafuncs-0.0.83.dist-info/RECORD +26 -0
- oafuncs/oa_down/test.py +0 -151
- oafuncs/oa_s/__init__.py +0 -23
- oafuncs/oa_s/oa_cmap.py +0 -163
- oafuncs/oa_s/oa_data.py +0 -187
- oafuncs/oa_s/oa_draw.py +0 -451
- oafuncs/oa_s/oa_file.py +0 -332
- oafuncs/oa_s/oa_help.py +0 -39
- oafuncs/oa_s/oa_nc.py +0 -410
- oafuncs/oa_s/oa_python.py +0 -107
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/__init__.py" +0 -26
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_cmap.py" +0 -163
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_data.py" +0 -187
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/__init__.py" +0 -20
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/hycom_3hourly.py" +0 -1176
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/literature.py" +0 -332
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/test_ua.py" +0 -151
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_draw.py" +0 -451
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_file.py" +0 -332
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_help.py" +0 -39
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_nc.py" +0 -410
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_python.py" +0 -107
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/__init__.py" +0 -21
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/meteorological.py" +0 -168
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/ocean.py" +0 -158
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/scientific.py" +0 -139
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/__init__.py" +0 -18
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/email.py" +0 -114
- oafuncs-0.0.81.dist-info/METADATA +0 -918
- oafuncs-0.0.81.dist-info/RECORD +0 -51
- {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/LICENSE.txt +0 -0
- {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/WHEEL +0 -0
- {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-
|
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__ = [
|
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
|
-
|
26
|
-
|
27
|
-
|
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(
|
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 ==
|
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(
|
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
|
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
|
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(
|
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
|
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
|
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
|
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
|
231
|
+
print(f"成功清空文件夹: {folder_path}")
|
227
232
|
except Exception as e:
|
228
|
-
print(f
|
233
|
+
print(f"清空文件夹失败: {folder_path}")
|
229
234
|
print(e)
|
230
235
|
|
231
236
|
|
232
237
|
# ** 清理空文件夹
|
233
|
-
def
|
234
|
-
|
238
|
+
def remove_empty_folder(path, print_info=1):
|
239
|
+
"""
|
235
240
|
# 描述:清理空文件夹
|
236
241
|
# 使用示例
|
237
|
-
|
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
|
287
|
+
print(f"成功删除文件: {file_path}")
|
283
288
|
except Exception as e:
|
284
|
-
print(f
|
289
|
+
print(f"删除文件失败: {file_path}")
|
285
290
|
print(e)
|
286
291
|
else:
|
287
|
-
print(f
|
292
|
+
print(f"文件不存在: {file_path}")
|
288
293
|
|
289
294
|
|
290
|
-
|
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
|
-
|
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
|
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-
|
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.
|
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: 查看
|
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=
|
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__+
|
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", "
|
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
|
-
|
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
|
-
|
87
|
-
|
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
|
-
|
100
|
+
|
90
101
|
return scale_factor, add_offset
|
91
102
|
|
92
103
|
|
93
|
-
def write2nc(file, data, varname=None, coords=None, mode=
|
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
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
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.
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
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
|
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"]
|
212
|
-
|
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
|
366
|
+
def convert_longitude(ds, lon_name="longitude", convert=180):
|
402
367
|
"""
|
403
|
-
|
368
|
+
将经度数组转换为指定的范围。
|
404
369
|
|
405
370
|
参数:
|
406
|
-
|
371
|
+
ds (xarray.Dataset): 包含经度数据的xarray数据集。
|
372
|
+
lon_name (str): 经度变量的名称,默认为"longitude"。
|
373
|
+
convert (int): 转换目标范围,可以是180或360,默认为180。
|
407
374
|
|
408
375
|
返回值:
|
409
|
-
|
376
|
+
xarray.Dataset: 经度转换后的xarray数据集。
|
410
377
|
"""
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
ds = ds.assign_coords(
|
419
|
-
|
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):
|