oafuncs 0.0.97.4__py3-none-any.whl → 0.0.97.6__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 +2 -13
- oafuncs/_script/auto_optimized_parallel_executor.py +459 -0
- oafuncs/_script/parallel_example_usage.py +83 -0
- oafuncs/_script/replace_file_concent.py +151 -0
- oafuncs/oa_date.py +90 -0
- oafuncs/oa_down/hycom_3hourly.py +2 -2
- oafuncs/oa_down/hycom_3hourly_20250129.py +2 -2
- oafuncs/oa_down/literature.py +1 -1
- oafuncs/oa_draw.py +32 -7
- oafuncs/oa_file.py +80 -10
- oafuncs/oa_nc.py +32 -27
- oafuncs/oa_tool/__init__.py +1 -13
- oafuncs/oa_tool/parallel.py +479 -6
- {oafuncs-0.0.97.4.dist-info → oafuncs-0.0.97.6.dist-info}/METADATA +4 -2
- {oafuncs-0.0.97.4.dist-info → oafuncs-0.0.97.6.dist-info}/RECORD +18 -15
- {oafuncs-0.0.97.4.dist-info → oafuncs-0.0.97.6.dist-info}/WHEEL +1 -1
- oafuncs/oa_tool/time.py +0 -22
- {oafuncs-0.0.97.4.dist-info → oafuncs-0.0.97.6.dist-info/licenses}/LICENSE.txt +0 -0
- {oafuncs-0.0.97.4.dist-info → oafuncs-0.0.97.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# coding=utf-8
|
3
|
+
"""
|
4
|
+
Author: Liu Kun && 16031215@qq.com
|
5
|
+
Date: 2025-03-21 10:02:32
|
6
|
+
LastEditors: Liu Kun && 16031215@qq.com
|
7
|
+
LastEditTime: 2025-03-21 10:02:33
|
8
|
+
FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\_script\\replace_file_concent.py
|
9
|
+
Description:
|
10
|
+
EditPlatform: vscode
|
11
|
+
ComputerInfo: XPS 15 9510
|
12
|
+
SystemInfo: Windows 11
|
13
|
+
Python Version: 3.12
|
14
|
+
"""
|
15
|
+
|
16
|
+
import datetime
|
17
|
+
import os
|
18
|
+
import re
|
19
|
+
from pathlib import Path
|
20
|
+
|
21
|
+
from rich import print
|
22
|
+
|
23
|
+
|
24
|
+
def _prepare_file_operation(source_file, target_dir, new_name=None):
|
25
|
+
"""
|
26
|
+
准备文件操作的公共逻辑
|
27
|
+
|
28
|
+
参数:
|
29
|
+
source_file: 源文件路径
|
30
|
+
target_dir: 目标目录路径
|
31
|
+
new_name: 新文件名,如果为None则使用原文件名
|
32
|
+
|
33
|
+
返回:
|
34
|
+
target_file: 目标文件路径
|
35
|
+
"""
|
36
|
+
os.makedirs(target_dir, exist_ok=True)
|
37
|
+
if new_name is None:
|
38
|
+
return os.path.join(target_dir, os.path.basename(source_file))
|
39
|
+
else:
|
40
|
+
return os.path.join(target_dir, new_name)
|
41
|
+
|
42
|
+
|
43
|
+
def replace_config_values(source_file, target_dir, param_dict, new_name=None):
|
44
|
+
"""
|
45
|
+
批量修改配置参数并保存到新路径(适用于等号赋值格式的参数)
|
46
|
+
|
47
|
+
参数:
|
48
|
+
source_file: 源文件路径
|
49
|
+
target_dir: 目标目录路径
|
50
|
+
param_dict: 要修改的参数字典 {参数名: 新值}
|
51
|
+
new_name: 新文件名,如果为None则使用原文件名
|
52
|
+
|
53
|
+
返回:
|
54
|
+
set: 成功修改的参数集合
|
55
|
+
"""
|
56
|
+
try:
|
57
|
+
target_file = _prepare_file_operation(source_file, target_dir, new_name)
|
58
|
+
|
59
|
+
with open(source_file, "r") as f:
|
60
|
+
lines = f.readlines()
|
61
|
+
|
62
|
+
modified = set()
|
63
|
+
for i in range(len(lines)):
|
64
|
+
line = lines[i]
|
65
|
+
stripped = line.lstrip()
|
66
|
+
|
67
|
+
# 跳过注释行和空行
|
68
|
+
if stripped.startswith(("!", "#", ";", "%")) or not stripped.strip():
|
69
|
+
continue
|
70
|
+
|
71
|
+
# 匹配所有参数
|
72
|
+
for param, new_val in param_dict.items():
|
73
|
+
# 构造动态正则表达式
|
74
|
+
pattern = re.compile(r'^(\s*{})(\s*=\s*)([\'"]?)(.*?)(\3)(\s*(!.*)?)$'.format(re.escape(param)), flags=re.IGNORECASE)
|
75
|
+
|
76
|
+
match = pattern.match(line.rstrip("\n"))
|
77
|
+
if match and param not in modified:
|
78
|
+
# 构造新行(保留原始格式)
|
79
|
+
new_line = f"{match.group(1)}{match.group(2)}{match.group(3)}{new_val}{match.group(5)}{match.group(6) or ''}\n"
|
80
|
+
lines[i] = new_line
|
81
|
+
modified.add(param)
|
82
|
+
break # 每行最多处理一个参数
|
83
|
+
|
84
|
+
with open(target_file, "w") as f:
|
85
|
+
f.writelines(lines)
|
86
|
+
|
87
|
+
print(f"[green]已将参数替换到新文件:{target_file}[/green]")
|
88
|
+
return modified
|
89
|
+
except Exception as e:
|
90
|
+
print(f"[red]替换参数时出错:{str(e)}[/red]")
|
91
|
+
return set()
|
92
|
+
|
93
|
+
|
94
|
+
def replace_direct_content(source_file, target_dir, content_dict, key_value=False, new_name=None):
|
95
|
+
"""
|
96
|
+
直接替换文件中的指定内容并保存到新路径
|
97
|
+
|
98
|
+
参数:
|
99
|
+
source_file: 源文件路径
|
100
|
+
target_dir: 目标目录路径
|
101
|
+
content_dict: 要替换的内容字典 {旧内容: 新内容}
|
102
|
+
key_value: 是否按键值对方式替换参数
|
103
|
+
new_name: 新文件名,如果为None则使用原文件名
|
104
|
+
|
105
|
+
返回:
|
106
|
+
bool: 替换是否成功
|
107
|
+
"""
|
108
|
+
try:
|
109
|
+
if key_value:
|
110
|
+
return len(replace_config_values(source_file, target_dir, content_dict, new_name)) > 0
|
111
|
+
|
112
|
+
target_file = _prepare_file_operation(source_file, target_dir, new_name)
|
113
|
+
|
114
|
+
with open(source_file, "r") as f:
|
115
|
+
content = f.read()
|
116
|
+
|
117
|
+
# 直接替换指定内容
|
118
|
+
for old_content, new_content in content_dict.items():
|
119
|
+
content = content.replace(old_content, new_content)
|
120
|
+
|
121
|
+
with open(target_file, "w") as f:
|
122
|
+
f.write(content)
|
123
|
+
|
124
|
+
print(f"[green]已将内容替换到新文件:{target_file}[/green]")
|
125
|
+
return True
|
126
|
+
except Exception as e:
|
127
|
+
print(f"[red]替换内容时出错:{str(e)}[/red]")
|
128
|
+
return False
|
129
|
+
|
130
|
+
|
131
|
+
if __name__ == "__main__":
|
132
|
+
control_file = Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file")
|
133
|
+
target_dir = r"/data/hejx/liukun/Work/Model/cas_esm/run"
|
134
|
+
|
135
|
+
force_time = 2023072900
|
136
|
+
ini_time = datetime.datetime.strptime(str(force_time), "%Y%m%d%H")
|
137
|
+
oisst_time = ini_time - datetime.timedelta(days=1) # 需要在前一天
|
138
|
+
|
139
|
+
replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "atm_in", target_dir=target_dir, param_dict={"ncdata": f"/data/hejx/liukun/Work/Model/cas_esm/data/IAP_ncep2_181x360_{ini_time.strftime('%Y%m%d')}_00_00_L35.nc"})
|
140
|
+
|
141
|
+
replace_direct_content(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "docn.stream.txt", target_dir=target_dir, content_dict={"oisst.forecast.20230727.nc": f"oisst.forecast.{oisst_time.strftime('%Y%m%d')}.nc"})
|
142
|
+
|
143
|
+
replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "drv_in", target_dir=target_dir, param_dict={"start_ymd": f"{ini_time.strftime('%Y%m%d')}"})
|
144
|
+
|
145
|
+
replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "ice_in", target_dir=target_dir, param_dict={"stream_fldfilename": f"/data/hejx/liukun/Work/Model/cas_esm/data/oisst.forecast.{ini_time.strftime('%Y%m%d')}.nc"})
|
146
|
+
|
147
|
+
replace_config_values(
|
148
|
+
source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "lnd_in",
|
149
|
+
target_dir=target_dir,
|
150
|
+
param_dict={"fini": f"/data/hejx/liukun/Work/Model/cas_esm/run_p1x1/colm-spinup-colm-restart-{ini_time.strftime('%Y-%m-%d')}-00000", "fsbc": f"/data/hejx/liukun/Work/Model/cas_esm/run_p1x1/colm-spinup-colm-restart-{ini_time.strftime('%Y-%m-%d')}-00000-sbc"},
|
151
|
+
)
|
oafuncs/oa_date.py
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# coding=utf-8
|
3
|
+
"""
|
4
|
+
Author: Liu Kun && 16031215@qq.com
|
5
|
+
Date: 2025-03-27 16:56:57
|
6
|
+
LastEditors: Liu Kun && 16031215@qq.com
|
7
|
+
LastEditTime: 2025-03-27 16:56:57
|
8
|
+
FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_date.py
|
9
|
+
Description:
|
10
|
+
EditPlatform: vscode
|
11
|
+
ComputerInfo: XPS 15 9510
|
12
|
+
SystemInfo: Windows 11
|
13
|
+
Python Version: 3.12
|
14
|
+
"""
|
15
|
+
|
16
|
+
import calendar
|
17
|
+
import datetime
|
18
|
+
|
19
|
+
__all__ = ["get_days_in_month", "generate_hour_list", "adjust_time"]
|
20
|
+
|
21
|
+
|
22
|
+
def get_days_in_month(year, month):
|
23
|
+
return calendar.monthrange(year, month)[1]
|
24
|
+
|
25
|
+
|
26
|
+
def generate_hour_list(start_date, end_date, interval_hours=6):
|
27
|
+
"""
|
28
|
+
Generate a list of datetime strings with a specified interval in hours.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
start_date (str): Start date in the format "%Y%m%d%H".
|
32
|
+
end_date (str): End date in the format "%Y%m%d%H".
|
33
|
+
interval_hours (int): Interval in hours between each datetime.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
list: List of datetime strings in the format "%Y%m%d%H".
|
37
|
+
"""
|
38
|
+
date_s = datetime.datetime.strptime(start_date, "%Y%m%d%H")
|
39
|
+
date_e = datetime.datetime.strptime(end_date, "%Y%m%d%H")
|
40
|
+
date_list = []
|
41
|
+
while date_s <= date_e:
|
42
|
+
date_list.append(date_s.strftime("%Y%m%d%H"))
|
43
|
+
date_s += datetime.timedelta(hours=interval_hours)
|
44
|
+
return date_list
|
45
|
+
|
46
|
+
|
47
|
+
def adjust_time(initial_time, amount, time_unit="hours", output_format=None):
|
48
|
+
"""
|
49
|
+
Adjust a given initial time by adding a specified amount of time.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
initial_time (str): Initial time in the format "yyyymmdd" to "yyyymmddHHMMSS".
|
53
|
+
Missing parts are assumed to be "0".
|
54
|
+
amount (int): The amount of time to add.
|
55
|
+
time_unit (str): The unit of time to add ("seconds", "minutes", "hours", "days").
|
56
|
+
output_format (str, optional): Custom output format for the adjusted time. Defaults to None.
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
str: The adjusted time as a string, formatted according to the output_format or time unit.
|
60
|
+
"""
|
61
|
+
# Normalize the input time to "yyyymmddHHMMSS" format
|
62
|
+
time_format = "%Y%m%d%H%M%S"
|
63
|
+
initial_time = initial_time.ljust(14, "0")
|
64
|
+
time_obj = datetime.datetime.strptime(initial_time, time_format)
|
65
|
+
|
66
|
+
# Add the specified amount of time
|
67
|
+
if time_unit == "seconds":
|
68
|
+
time_obj += datetime.timedelta(seconds=amount)
|
69
|
+
elif time_unit == "minutes":
|
70
|
+
time_obj += datetime.timedelta(minutes=amount)
|
71
|
+
elif time_unit == "hours":
|
72
|
+
time_obj += datetime.timedelta(hours=amount)
|
73
|
+
elif time_unit == "days":
|
74
|
+
time_obj += datetime.timedelta(days=amount)
|
75
|
+
else:
|
76
|
+
raise ValueError("Invalid time unit. Use 'seconds', 'minutes', 'hours', or 'days'.")
|
77
|
+
|
78
|
+
# Determine the output format
|
79
|
+
if output_format:
|
80
|
+
return time_obj.strftime(output_format)
|
81
|
+
else:
|
82
|
+
if time_unit == "seconds":
|
83
|
+
default_format = "%Y%m%d%H%M%S"
|
84
|
+
elif time_unit == "minutes":
|
85
|
+
default_format = "%Y%m%d%H%M"
|
86
|
+
elif time_unit == "hours":
|
87
|
+
default_format = "%Y%m%d%H"
|
88
|
+
elif time_unit == "days":
|
89
|
+
default_format = "%Y%m%d"
|
90
|
+
return time_obj.strftime(default_format)
|
oafuncs/oa_down/hycom_3hourly.py
CHANGED
@@ -35,8 +35,8 @@ import xarray as xr
|
|
35
35
|
from rich import print
|
36
36
|
from rich.progress import Progress
|
37
37
|
|
38
|
-
from oafuncs.
|
39
|
-
from oafuncs.
|
38
|
+
from oafuncs._oa_down.idm import downloader as idm_downloader
|
39
|
+
from oafuncs._oa_down.user_agent import get_ua
|
40
40
|
from oafuncs.oa_file import file_size, mean_size
|
41
41
|
from oafuncs.oa_nc import check as check_nc
|
42
42
|
from oafuncs.oa_nc import modify as modify_nc
|
@@ -34,8 +34,8 @@ import xarray as xr
|
|
34
34
|
from rich import print
|
35
35
|
from rich.progress import Progress
|
36
36
|
|
37
|
-
from oafuncs.
|
38
|
-
from oafuncs.
|
37
|
+
from oafuncs._oa_down.idm import downloader as idm_downloader
|
38
|
+
from oafuncs._oa_down.user_agent import get_ua
|
39
39
|
from oafuncs.oa_file import file_size, mean_size
|
40
40
|
from oafuncs.oa_nc import check as check_nc
|
41
41
|
from oafuncs.oa_nc import modify as modify_nc
|
oafuncs/oa_down/literature.py
CHANGED
@@ -22,7 +22,7 @@ import pandas as pd
|
|
22
22
|
import requests
|
23
23
|
from rich import print
|
24
24
|
from rich.progress import track
|
25
|
-
from oafuncs.
|
25
|
+
from oafuncs._oa_down.user_agent import get_ua
|
26
26
|
from oafuncs.oa_file import remove
|
27
27
|
from oafuncs.oa_data import ensure_list
|
28
28
|
|
oafuncs/oa_draw.py
CHANGED
@@ -76,26 +76,51 @@ def fig_minus(ax_x=None, ax_y=None, cbar=None, decimal=None, add_space=False):
|
|
76
76
|
|
77
77
|
|
78
78
|
# ** 将生成图片/已有图片制作成动图
|
79
|
-
def gif(image_list: list, gif_name: str, duration=
|
79
|
+
def gif(image_list: list, gif_name: str, duration=200, resize=None): # 制作动图,默认间隔0.2
|
80
80
|
"""
|
81
81
|
Description
|
82
82
|
Make gif from images
|
83
83
|
Parameters
|
84
84
|
image_list : list, list of images
|
85
85
|
gif_name : str, name of gif
|
86
|
-
duration : float, duration of each frame
|
86
|
+
duration : float, duration of each frame, units: ms
|
87
|
+
resize : tuple, (width, height) to resize images, if None, use first image size
|
87
88
|
Returns
|
88
89
|
None
|
89
90
|
Example
|
90
91
|
gif(["1.png", "2.png"], "test.gif", duration=0.2)
|
91
92
|
"""
|
92
93
|
import imageio.v2 as imageio
|
94
|
+
import numpy as np
|
95
|
+
from PIL import Image
|
93
96
|
|
94
97
|
frames = []
|
98
|
+
|
99
|
+
# 获取目标尺寸
|
100
|
+
if resize is None and image_list:
|
101
|
+
# 使用第一张图片的尺寸作为标准
|
102
|
+
with Image.open(image_list[0]) as img:
|
103
|
+
resize = img.size
|
104
|
+
|
105
|
+
# 读取并调整所有图片的尺寸
|
95
106
|
for image_name in image_list:
|
96
|
-
|
97
|
-
|
98
|
-
|
107
|
+
with Image.open(image_name) as img:
|
108
|
+
if resize:
|
109
|
+
img = img.resize(resize, Image.LANCZOS)
|
110
|
+
frames.append(np.array(img))
|
111
|
+
|
112
|
+
# 修改此处:明确使用 duration 值,并将其作为每帧的持续时间(以秒为单位)
|
113
|
+
# 某些版本的 imageio 可能需要以毫秒为单位,或者使用 fps 参数
|
114
|
+
try:
|
115
|
+
# 先尝试直接使用 duration 参数(以秒为单位)
|
116
|
+
imageio.mimsave(gif_name, frames, format="GIF", duration=duration)
|
117
|
+
except Exception as e:
|
118
|
+
print(f"尝试使用fps参数替代duration: {e}")
|
119
|
+
# 如果失败,尝试使用 fps 参数(fps = 1/duration)
|
120
|
+
fps = 1.0 / duration if duration > 0 else 5.0
|
121
|
+
imageio.mimsave(gif_name, frames, format="GIF", fps=fps)
|
122
|
+
|
123
|
+
print(f"Gif制作完成!尺寸: {resize}, 帧间隔: {duration}毫秒")
|
99
124
|
return
|
100
125
|
|
101
126
|
|
@@ -171,8 +196,8 @@ def add_cartopy(ax, lon=None, lat=None, projection=ccrs.PlateCarree(), gridlines
|
|
171
196
|
|
172
197
|
# set extent
|
173
198
|
if lon is not None and lat is not None:
|
174
|
-
lon_min, lon_max =
|
175
|
-
lat_min, lat_max =
|
199
|
+
lon_min, lon_max = np.nanmin(lon), np.nanmax(lon)
|
200
|
+
lat_min, lat_max = np.nanmin(lat), np.nanmax(lat)
|
176
201
|
ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=projection)
|
177
202
|
|
178
203
|
|
oafuncs/oa_file.py
CHANGED
@@ -17,9 +17,11 @@ import glob
|
|
17
17
|
import os
|
18
18
|
import re
|
19
19
|
import shutil
|
20
|
+
|
21
|
+
|
20
22
|
from rich import print
|
21
23
|
|
22
|
-
__all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir"]
|
24
|
+
__all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content", "move_file"]
|
23
25
|
|
24
26
|
|
25
27
|
# ** 查找文件,支持通配符
|
@@ -155,6 +157,55 @@ def copy_file(src_pattern, dst):
|
|
155
157
|
print(f"Copy and rename file or directory: {src_file} -> {dst_file}")
|
156
158
|
|
157
159
|
|
160
|
+
|
161
|
+
# ** 移动文件或目录,支持通配符
|
162
|
+
def move_file(src_pattern, dst):
|
163
|
+
"""
|
164
|
+
# 描述:移动文件或目录,支持通配符
|
165
|
+
# 使用示例
|
166
|
+
# move_file(r'/data/hejx/liukun/era5/*', r'/data/hejx/liukun/Test/')
|
167
|
+
# move_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test/py.o')
|
168
|
+
# move_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test')
|
169
|
+
param {*} src_pattern # 源文件或目录
|
170
|
+
param {*} dst # 目标文件或目录
|
171
|
+
"""
|
172
|
+
src_pattern = str(src_pattern)
|
173
|
+
# 使用glob.glob来处理可能包含通配符的src
|
174
|
+
src_files = glob.glob(src_pattern)
|
175
|
+
if not src_files:
|
176
|
+
raise FileNotFoundError("File does not exist: {}".format(src_pattern))
|
177
|
+
|
178
|
+
# 判断dst是路径还是包含文件名的路径
|
179
|
+
if os.path.isdir(dst):
|
180
|
+
# 如果dst是路径,则保持源文件的文件名
|
181
|
+
dst_dir = dst
|
182
|
+
for src_file in src_files:
|
183
|
+
src_file_basename = os.path.basename(src_file)
|
184
|
+
dst_file = os.path.join(dst_dir, src_file_basename)
|
185
|
+
if os.path.exists(dst_file):
|
186
|
+
if os.path.isdir(dst_file):
|
187
|
+
shutil.rmtree(dst_file)
|
188
|
+
else:
|
189
|
+
os.remove(dst_file)
|
190
|
+
shutil.move(src_file, dst_file)
|
191
|
+
print(f"Move file or directory: {src_file} -> {dst_file}")
|
192
|
+
else:
|
193
|
+
# 如果dst包含文件名,则移动后重命名
|
194
|
+
dst_dir = os.path.dirname(dst)
|
195
|
+
os.makedirs(dst_dir, exist_ok=True)
|
196
|
+
# 只处理第一个匹配的文件
|
197
|
+
src_file = src_files[0]
|
198
|
+
dst_file = dst
|
199
|
+
if os.path.exists(dst_file):
|
200
|
+
if os.path.isdir(dst_file):
|
201
|
+
shutil.rmtree(dst_file)
|
202
|
+
else:
|
203
|
+
os.remove(dst_file)
|
204
|
+
shutil.move(src_file, dst_file)
|
205
|
+
print(f"Move and rename file or directory: {src_file} -> {dst_file}")
|
206
|
+
|
207
|
+
|
208
|
+
|
158
209
|
# ** 重命名文件,支持通配符
|
159
210
|
def rename_file(directory, old_str, new_str):
|
160
211
|
"""
|
@@ -217,13 +268,13 @@ def make_dir(directory):
|
|
217
268
|
"""
|
218
269
|
Description:
|
219
270
|
Create a directory if it does not exist
|
220
|
-
|
271
|
+
|
221
272
|
Parameters:
|
222
273
|
directory: The directory path to create
|
223
|
-
|
274
|
+
|
224
275
|
Returns:
|
225
276
|
None
|
226
|
-
|
277
|
+
|
227
278
|
Example:
|
228
279
|
make_dir(r"E:\\Data\\2024\\09\\17\\var1")
|
229
280
|
"""
|
@@ -355,7 +406,7 @@ def file_size(file_path, unit="KB"):
|
|
355
406
|
if not os.path.exists(file_path):
|
356
407
|
# return "文件不存在"
|
357
408
|
# print(f"文件不存在: {file_path}\n返回0.0")
|
358
|
-
print(f
|
409
|
+
print(f"File does not exist: {file_path}\nReturn 0.0")
|
359
410
|
return 0.0
|
360
411
|
|
361
412
|
# 获取文件大小(字节)
|
@@ -378,25 +429,25 @@ def file_size(file_path, unit="KB"):
|
|
378
429
|
|
379
430
|
|
380
431
|
# ** 计算文件夹下指定相关文件的平均大小
|
381
|
-
def mean_size(parent_path,fname,max_num=None,unit="KB"):
|
432
|
+
def mean_size(parent_path, fname, max_num=None, unit="KB"):
|
382
433
|
"""
|
383
434
|
Description:
|
384
435
|
Calculate the average size of the specified related files in the folder
|
385
|
-
|
436
|
+
|
386
437
|
Parameters:
|
387
438
|
parent_path: The parent path where the files are located
|
388
439
|
fname: The file name pattern to search for
|
389
440
|
max_num: The maximum number of files to search for
|
390
441
|
unit: The unit of the file size, default is "KB"
|
391
|
-
|
442
|
+
|
392
443
|
Returns:
|
393
444
|
The average size
|
394
445
|
"""
|
395
446
|
flist = find_file(parent_path, fname)
|
396
447
|
if flist:
|
397
448
|
if max_num:
|
398
|
-
flist = flist[:int(max_num)]
|
399
|
-
size_list = [file_size(f,unit) for f in flist if file_size(f,unit) > 0]
|
449
|
+
flist = flist[: int(max_num)]
|
450
|
+
size_list = [file_size(f, unit) for f in flist if file_size(f, unit) > 0]
|
400
451
|
if size_list:
|
401
452
|
return sum(size_list) / len(size_list)
|
402
453
|
else:
|
@@ -405,6 +456,25 @@ def mean_size(parent_path,fname,max_num=None,unit="KB"):
|
|
405
456
|
return 0.0
|
406
457
|
|
407
458
|
|
459
|
+
def replace_content(source_file, content_dict, key_value=False, target_dir=None, new_name=None):
|
460
|
+
"""
|
461
|
+
直接替换文件中的指定内容并保存到新路径
|
462
|
+
|
463
|
+
参数:
|
464
|
+
source_file: 源文件路径
|
465
|
+
target_dir: 目标目录路径
|
466
|
+
content_dict: 要替换的内容字典 {旧内容: 新内容}
|
467
|
+
key_value: 是否按键值对方式替换参数
|
468
|
+
|
469
|
+
返回:
|
470
|
+
bool: 替换是否成功
|
471
|
+
"""
|
472
|
+
from ._script.replace_file_concent import replace_direct_content
|
473
|
+
if target_dir is None:
|
474
|
+
target_dir = os.path.dirname(source_file)
|
475
|
+
replace_direct_content(source_file, target_dir, content_dict, key_value=key_value, new_name=new_name)
|
476
|
+
|
477
|
+
|
408
478
|
if __name__ == "__main__":
|
409
479
|
# newpath = make_folder('D:/Data/2024/09/17/', 'var1', clear=1)
|
410
480
|
# print(newpath)
|
oafuncs/oa_nc.py
CHANGED
@@ -20,8 +20,6 @@ import numpy as np
|
|
20
20
|
import xarray as xr
|
21
21
|
from rich import print
|
22
22
|
|
23
|
-
from ._script.plot_dataset import func_plot_dataset
|
24
|
-
|
25
23
|
__all__ = ["get_var", "extract", "save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw"]
|
26
24
|
|
27
25
|
|
@@ -280,15 +278,21 @@ def _modify_var(nc_file_path, variable_name, new_value):
|
|
280
278
|
"""
|
281
279
|
try:
|
282
280
|
# Open the NetCDF file
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
281
|
+
with nc.Dataset(nc_file_path, "r+") as dataset:
|
282
|
+
# Check if the variable exists
|
283
|
+
if variable_name not in dataset.variables:
|
284
|
+
raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
|
285
|
+
# Get the variable to be modified
|
286
|
+
variable = dataset.variables[variable_name]
|
287
|
+
# Check if the shape of the new value matches the variable's shape
|
288
|
+
if variable.shape != new_value.shape:
|
289
|
+
raise ValueError(f"Shape mismatch: Variable '{variable_name}' has shape {variable.shape}, "
|
290
|
+
f"but new value has shape {new_value.shape}.")
|
291
|
+
# Modify the value of the variable
|
292
|
+
variable[:] = new_value
|
293
|
+
print(f"Successfully modified variable '{variable_name}' in '{nc_file_path}'.")
|
290
294
|
except Exception as e:
|
291
|
-
print(f"An error occurred while modifying variable {variable_name} in {nc_file_path}: {e}")
|
295
|
+
print(f"An error occurred while modifying variable '{variable_name}' in '{nc_file_path}': {e}")
|
292
296
|
|
293
297
|
|
294
298
|
def _modify_attr(nc_file_path, variable_name, attribute_name, attribute_value):
|
@@ -306,21 +310,18 @@ def _modify_attr(nc_file_path, variable_name, attribute_name, attribute_value):
|
|
306
310
|
modify_attr('test.nc', 'temperature', 'long_name', 'Temperature in Celsius')
|
307
311
|
"""
|
308
312
|
try:
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
313
|
+
with nc.Dataset(nc_file_path, "r+") as ds:
|
314
|
+
# Check if the variable exists
|
315
|
+
if variable_name not in ds.variables:
|
316
|
+
raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
|
317
|
+
# Get the variable
|
318
|
+
variable = ds.variables[variable_name]
|
319
|
+
# Add or modify the attribute
|
316
320
|
variable.setncattr(attribute_name, attribute_value)
|
317
|
-
|
318
|
-
print(f"Adding attribute '{attribute_name}'...")
|
319
|
-
variable.setncattr(attribute_name, attribute_value)
|
320
|
-
|
321
|
-
ds.close()
|
321
|
+
print(f"Successfully modified attribute '{attribute_name}' of variable '{variable_name}' in '{nc_file_path}'.")
|
322
322
|
except Exception as e:
|
323
|
-
|
323
|
+
print(f"[red]Error:[/red] Failed to modify attribute '{attribute_name}' of variable '{variable_name}' "
|
324
|
+
f"in file '{nc_file_path}'. [bold]Details:[/bold] {e}")
|
324
325
|
|
325
326
|
|
326
327
|
def modify(nc_file, var_name, attr_name=None, new_value=None):
|
@@ -338,10 +339,13 @@ def modify(nc_file, var_name, attr_name=None, new_value=None):
|
|
338
339
|
modify('test.nc', 'temperature', 'long_name', 'Temperature in Celsius')
|
339
340
|
modify('test.nc', 'temperature', None, np.random.rand(100, 50))
|
340
341
|
"""
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
342
|
+
try:
|
343
|
+
if attr_name is None:
|
344
|
+
_modify_var(nc_file, var_name, new_value)
|
345
|
+
else:
|
346
|
+
_modify_attr(nc_file, var_name, attr_name, new_value)
|
347
|
+
except Exception as e:
|
348
|
+
print(f"An error occurred while modifying '{var_name}' in '{nc_file}': {e}")
|
345
349
|
|
346
350
|
|
347
351
|
def rename(ncfile_path, old_name, new_name):
|
@@ -499,6 +503,7 @@ def draw(output_dir=None, dataset=None, ncfile=None, xyzt_dims=("longitude", "la
|
|
499
503
|
Example:
|
500
504
|
draw(ncfile, output_dir, x_dim="longitude", y_dim="latitude", z_dim="level", t_dim="time", fixed_colorscale=False)
|
501
505
|
"""
|
506
|
+
from ._script.plot_dataset import func_plot_dataset
|
502
507
|
if output_dir is None:
|
503
508
|
output_dir = str(os.getcwd())
|
504
509
|
if isinstance(xyzt_dims, (list, tuple)):
|
oafuncs/oa_tool/__init__.py
CHANGED
@@ -1,19 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
# coding=utf-8
|
3
|
-
|
4
|
-
Author: Liu Kun && 16031215@qq.com
|
5
|
-
Date: 2024-11-21 09:48:00
|
6
|
-
LastEditors: Liu Kun && 16031215@qq.com
|
7
|
-
LastEditTime: 2025-01-11 20:09:09
|
8
|
-
FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_tool\\__init__.py
|
9
|
-
Description:
|
10
|
-
EditPlatform: vscode
|
11
|
-
ComputerInfo: XPS 15 9510
|
12
|
-
SystemInfo: Windows 11
|
13
|
-
Python Version: 3.12
|
14
|
-
"""
|
3
|
+
|
15
4
|
|
16
5
|
# 会导致OAFuncs直接导入所有函数,不符合模块化设计
|
17
6
|
from .email import *
|
18
7
|
from .parallel import *
|
19
|
-
from .time import *
|