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.
- oafuncs/__init__.py +27 -12
- oafuncs/oa_cmap.py +31 -52
- oafuncs/oa_data.py +107 -28
- oafuncs/oa_down/hycom_3hourly.py +151 -34
- oafuncs/oa_draw.py +148 -96
- oafuncs/oa_file.py +61 -50
- oafuncs/oa_nc.py +131 -53
- {oafuncs-0.0.80.dist-info → oafuncs-0.0.82.dist-info}/METADATA +1 -2
- oafuncs-0.0.82.dist-info/RECORD +24 -0
- oafuncs-0.0.82.dist-info/top_level.txt +1 -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.80.dist-info/RECORD +0 -51
- oafuncs-0.0.80.dist-info/top_level.txt +0 -2
- {oafuncs-0.0.80.dist-info → oafuncs-0.0.82.dist-info}/LICENSE.txt +0 -0
- {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-
|
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_folders", "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
|
|
@@ -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
|
229
|
+
print(f"成功清空文件夹: {folder_path}")
|
227
230
|
except Exception as e:
|
228
|
-
print(f
|
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
|
285
|
+
print(f"成功删除文件: {file_path}")
|
283
286
|
except Exception as e:
|
284
|
-
print(f
|
287
|
+
print(f"删除文件失败: {file_path}")
|
285
288
|
print(e)
|
286
289
|
else:
|
287
|
-
print(f
|
290
|
+
print(f"文件不存在: {file_path}")
|
288
291
|
|
289
292
|
|
290
|
-
|
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
|
-
|
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
|
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
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
144
|
+
var = ncfile.createVariable(dim, _numpy_to_nc_type(coord_data.dtype), (dim,), **kwargs)
|
145
|
+
var[:] = np.array(coord_data)
|
121
146
|
|
122
|
-
|
123
|
-
|
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.
|
131
|
-
|
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
|
145
|
-
|
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.
|
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
|