活动介绍

AttributeError: 'DataFrame' object has no attribute 'extend'

时间: 2023-09-16 18:14:59 浏览: 344
这个错误是因为 DataFrame 对象没有 extend 属性。extend 属性是列表才会有的。 你可以考虑将 DataFrame 对象转换为列表,然后再使用 extend 方法。例如: ``` df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']}) my_list = df.values.tolist() # 将 DataFrame 转为列表 my_list.extend([[4, 'd']]) # 使用 extend 方法 ``` 这样就不会出现 `'DataFrame' object has no attribute 'extend'` 的错误了。
相关问题

AttributeError: 'Series' object has no attribute 'extend'

`AttributeError: 'Series' object has no attribute 'extend'` 是一个常见的Python错误,它通常发生在尝试对Pandas DataFrame或Series对象使用 `extend()` 方法时。`extend()` 是列表(list)的一个内置方法,用于添加多个元素到列表的末尾。然而,Series对象并不是列表类型,它们是Pandas库中的一个一维数组结构,用于存储同类型的值。 当你看到这个错误,意味着你试图在一个Series对象上调用了一个只适用于列表的方法。要解决这个问题,你需要确认以下几点: 1. 检查你是否正确地引用了Series对象,确保它是Series而不是列表。 2. 如果你想合并两个Series,你可以使用 `concat()` 或 `append()` 方法,而不是直接调用 `extend()`。 3. 如果你是想逐个添加元素,可能需要先转换Series为列表,然后进行操作。 例如,如果你有一个Series `s` 和另一个数据项 `new_value`,正确的做法是: ```python s = s.append(new_value) ``` 或者 ```python # 先转为list values_list = list(s) values_list.extend([new_value]) s = pd.Series(values_list) ```

AttributeError: 'ColorVisuals' object has no attribute 'crc'

这个错误是由于在代码中使用了一个名为'crc'的属性,但是'ColorVisuals'对象并没有这个属性。根据提供的引用内容,我没有找到与'ColorVisuals'对象和'crc'属性相关的信息。可能是代码中的某个地方出现了错误或者缺少了必要的引用。建议检查代码中的拼写错误、变量命名错误或者确保引用了正确的模块和类。如果问题仍然存在,可以提供更多的代码或者上下文信息以便更好地帮助解决问题。 #### 引用[.reference_title] - *1* *3* [Python AttributeError: ‘map‘ object has no attribute ‘extend](https://blog.csdn.net/wukai0909/article/details/125032036)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [已解决AttributeError: ‘DataFrame‘ object has no attribute ‘ix](https://blog.csdn.net/yuan2019035055/article/details/124558014)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
阅读全文

相关推荐

AttributeError: 'numpy.ndarray' object has no attribute 'fillna' 2025-07-19 17:07:14,762 - INFO - 目标分布: 0=713, 1=16 2025-07-19 17:07:14,770 - ERROR - 特征工程失败: 'numpy.ndarray' object has no attribute 'fillna' Traceback (most recent call last): File "d:\股票量化数据库\股票量化数据库\untitled4.py", line 232, in transform df['RSI14'] = ta.RSI(df_temp['close'].values, timeperiod=14).fillna(50) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'numpy.ndarray' object has no attribute 'fillna' 2025-07-19 17:07:14,778 - INFO - 目标分布: 0=724, 1=5 处理股票数据: 100%|██████████| 6723/6723 [02:17<00:00, 48.99it/s] 2025-07-19 17:07:19,387 - INFO - 数据集准备完成,样本数: 4285658 2025-07-19 17:07:20,151 - INFO - 目标分布: 0=4171023, 1=114635 2025-07-19 17:07:21,795 - INFO - 开始训练模型... 2025-07-19 17:07:22,508 - INFO - 类别不平衡处理: 正样本权重 = 36.39 2025-07-19 17:07:22,509 - INFO - 执行特征选择... [LightGBM] [Info] Number of positive: 114635, number of negative: 4171023 [LightGBM] [Info] Auto-choosing row-wise multi-threading, the overhead of testing was 0.021112 seconds. You can set force_row_wise=true to remove the overhead. And if memory is not enough, you can set force_col_wise=true. [LightGBM] [Info] Total Bins 2550 [LightGBM] [Info] Number of data points in the train set: 4285658, number of used features: 10 [LightGBM] [Info] [binary:BoostFromScore]: pavg=0.026749 -> initscore=-3.594163 [LightGBM] [Info] Start training from score -3.594163 2025-07-19 17:07:31,028 - INFO - 特征重要性: volatility 563 price_change 499 volume 491 volume_change 339 MA20 318 MA5 226 low 175 high 144 open 135 close 110 RSI_diff 0 day_of_week 0 advance_decline 0 MACD_RSI 0 price_vol_ratio 0 MACD_hist 0 VWAP 0 ATR14 0 MOM10 0 cluster 0 RSI14 0 month 0 2025-07-19 17:07:31,028 - INFO - 选择前 15 个特征: ['volatility', 'price_change', 'volume', 'volume_change', 'MA20', 'MA5', 'low', 'high', 'open', 'close', 'RSI_diff', 'day_of_week', 'advance_decline', 'MACD_RSI', 'price_vol_ratio'] 2025-07-19 17:07:32,098 - INFO - 开始参数搜索... Fitting 3 folds for each of 20 candidates, totalling 60 fits Traceback (most recent call last): File D:\Anaconda\Lib\site-packages\spyder_kernels\py3compat.py:356 in compat_exec exec(code, globals, locals) File d:\股票量化数据库\股票量化数据库\untitled4.py:687 main() File d:\股票量化数据库\股票量化数据库\untitled4.py:655 in main model = trainer.train_model(X_train, y_train, groups) File d:\股票量化数据库\股票量化数据库\untitled4.py:543 in train_model search.fit(X_selected, y) File D:\Anaconda\Lib\site-packages\sklearn\model_selection\_search.py:874 in fit self._run_search(evaluate_candidates) File D:\Anaconda\Lib\site-packages\sklearn\model_selection\_search.py:1768 in _run_search evaluate_candidates( File D:\Anaconda\Lib\site-packages\sklearn\model_selection\_search.py:821 in evaluate_candidates out = parallel( File D:\Anaconda\Lib\site-packages\sklearn\utils\parallel.py:63 in __call__ return super().__call__(iterable_with_config) File D:\Anaconda\Lib\site-packages\joblib\parallel.py:1098 in __call__ self.retrieve() File D:\Anaconda\Lib\site-packages\joblib\parallel.py:975 in retrieve self._output.extend(job.get(timeout=self.timeout)) File D:\Anaconda\Lib\site-packages\joblib\_parallel_backends.py:567 in wrap_future_result return future.result(timeout=timeout) File D:\Anaconda\Lib\concurrent\futures\_base.py:456 in result return self.__get_result() File D:\Anaconda\Lib\concurrent\futures\_base.py:401 in __get_result raise self._exception MemoryError: Unable to allocate 54.5 MiB for an array with shape (5, 1428553) and data type int64

参数调优进度: 0%| | 0/20 [00:00<?, ?it/s] --------------------------------------------------------------------------- _RemoteTraceback Traceback (most recent call last) _RemoteTraceback: """ Traceback (most recent call last): File "D:\Anaconda\Lib\site-packages\joblib\externals\loky\process_executor.py", line 428, in _process_worker r = call_item() ^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\joblib\externals\loky\process_executor.py", line 275, in __call__ return self.fn(*self.args, **self.kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\joblib\_parallel_backends.py", line 620, in __call__ return self.func(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\joblib\parallel.py", line 288, in __call__ return [func(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\joblib\parallel.py", line 288, in return [func(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\sklearn\utils\parallel.py", line 123, in __call__ return self.function(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda\Lib\site-packages\sklearn\model_selection\_validation.py", line 671, in _fit_and_score for k, v in parameters.items(): ^^^^^^^^^^^^^^^^ AttributeError: 'str' object has no attribute 'items' """ The above exception was the direct cause of the following exception: AttributeError Traceback (most recent call last) Cell In[18], line 39 32 # 使用带进度条的GridSearchCV 33 grid_search = ProgressGridSearchCV( 34 estimator=rf, 35 param_grid=param_grid, 36 cv=5, 37 n_jobs=-1 38 ) ---> 39 grid_search.fit(X_train, y_train) 40 results_df = pd.DataFrame(grid_search.cv_results_) 41 # 输出最优参数 Cell In[18], line 19, in ProgressGridSearchCV.fit(self, X, y, **fit_params) 15 self.pbar = tqdm(total=len(self.param_grid)*self.cv, 16 desc="参数调优进度") 18 # 调用父类fit方法 ---> 19 result = super().fit(X, y, **fit_params) 21 # 关闭进度条 22 self.pbar.close() File D:\Anaconda\Lib\site-packages\sklearn\model_selection\_search.py:874, in BaseSearchCV.fit(self, X, y, groups, **fit_params) 868 results = self._format_results( 869 all_candidate_params, n_splits, all_out, all_more_results 870 ) 872 return results --> 874 self._run_search(evaluate_candidates) 876 # multimetric is determined here because in the case of a callable 877 # self.scoring the return type is only known after calling 878 first_test_score = all_out[0]["test_scores"] Cell In[18], line 30, in ProgressGridSearchCV._run_search(self, evaluate_candidates) 27 for candidate in self.param_grid: 28 # 更新进度条 29 self.pbar.update(1) ---> 30 evaluate_candidates([candidate]) File D:\Anaconda\Lib\site-packages\sklearn\model_selection\_search.py:821, in BaseSearchCV.fit.<locals>.evaluate_candidates(candidate_params, cv, more_results) 813 if self.verbose > 0: 814 print( 815 "Fitting {0} folds for each of {1} candidates," 816 " totalling {2} fits".format( 817 n_splits, n_candidates, n_candidates * n_splits 818 ) 819 ) --> 821 out = parallel( 822 delayed(_fit_and_score)( 823 clone(base_estimator), 824 X, 825 y, 826 train=train, 827 test=test, 828 parameters=parameters, 829 split_progress=(split_idx, n_splits), 830 candidate_progress=(cand_idx, n_candidates), 831 **fit_and_score_kwargs, 832 ) 833 for (cand_idx, parameters), (split_idx, (train, test)) in product( 834 enumerate(candidate_params), enumerate(cv.split(X, y, groups)) 835 ) 836 ) 838 if len(out) < 1: 839 raise ValueError( 840 "No fits were performed. " 841 "Was the CV iterator empty? " 842 "Were there no candidates?" 843 ) File D:\Anaconda\Lib\site-packages\sklearn\utils\parallel.py:63, in Parallel.__call__(self, iterable) 58 config = get_config() 59 iterable_with_config = ( 60 (_with_config(delayed_func, config), args, kwargs) 61 for delayed_func, args, kwargs in iterable 62 ) ---> 63 return super().__call__(iterable_with_config) File D:\Anaconda\Lib\site-packages\joblib\parallel.py:1098, in Parallel.__call__(self, iterable) 1095 self._iterating = False 1097 with self._backend.retrieval_context(): -> 1098 self.retrieve() 1099 # Make sure that we get a last message telling us we are done 1100 elapsed_time = time.time() - self._start_time File D:\Anaconda\Lib\site-packages\joblib\parallel.py:975, in Parallel.retrieve(self) 973 try: 974 if getattr(self._backend, 'supports_timeout', False): --> 975 self._output.extend(job.get(timeout=self.timeout)) 976 else: 977 self._output.extend(job.get()) File D:\Anaconda\Lib\site-packages\joblib\_parallel_backends.py:567, in LokyBackend.wrap_future_result(future, timeout) 564 """Wrapper for Future.result to implement the same behaviour as 565 AsyncResults.get from multiprocessing.""" 566 try: --> 567 return future.result(timeout=timeout) 568 except CfTimeoutError as e: 569 raise TimeoutError from e File D:\Anaconda\Lib\concurrent\futures\_base.py:456, in Future.result(self, timeout) 454 raise CancelledError() 455 elif self._state == FINISHED: --> 456 return self.__get_result() 457 else: 458 raise TimeoutError() File D:\Anaconda\Lib\concurrent\futures\_base.py:401, in Future.__get_result(self) 399 if self._exception: 400 try: --> 401 raise self._exception 402 finally: 403 # Break a reference cycle with the exception in self._exception 404 self = None AttributeError: 'str' object has no attribute 'items'

import os from tkinter import Tk, filedialog from docx import Document import pandas as pd def process_docx(file_path): """处理单个docx文件,提取表格前31行数据""" doc = Document(file_path) # 检查表格是否存在 if not doc.tables: raise ValueError("文档中未找到任何表格") table = doc.tables[0] data = [] print(f"正在处理表格,总行数:{len(table.rows)}") # 调试输出 for row_idx, row in enumerate(table.rows[:31]): row_data = [] for cell in row.cells: if cell.text.strip() != '': row_data.append(cell.text.strip()) else: merged_content = find_merged_cell_content(table, row_idx, cell) row_data.append(merged_content) data.append(row_data) return data def find_merged_cell_content(table, row_idx, cell): """处理合并单元格内容获取""" for r in range(row_idx, -1, -1): try: target_cell = table.cell(r, cell.cell.column_index) if target_cell.text.strip() != '': return target_cell.text.strip() except IndexError : pass return '' def main(): root = Tk() root.withdraw() files = filedialog.askopenfilenames( title="选择需要处理的DOCX文件(可多选)", filetypes=[("Word Documents", "*.docx")] ) if not files: print("未选择任何文件,程序退出") return save_path = filedialog.asksaveasfilename( title="保存Excel文件", defaultextension=".xlsx", filetypes=[("Excel Files", "*.xlsx")] ) all_data = [] total_files = len(files) print(f"开始处理 {total_files} 个文件...") for i, file_path in enumerate(files, 1): try: file_data = process_docx(file_path) print(f"从 {os.path.basename(file_path)} 提取到 {len(file_data)} 行数据") # 调试输出 for row in file_data: row.insert(0, os.path.basename(file_path)) all_data.extend(file_data) print(f"已处理 [{i}/{total_files}] {os.path.basename(file_path)}") except Exception as e: print(f"处理失败:{file_path} | 错误类型:{type(e).__name__} | 错误信息:{str(e)}") if all_data: df = pd.DataFrame(all_data) df.to_excel(save_path, index=False, header=False) print(f"\n处理完成!共处理 {len(all_data)} 行数据,已保存至:{save_path}") else: print("\n警告:未提取到任何有效数据,请检查文件内容") if __name__ == "__main__": main()上述代码运行后提示错误信息’_Cell’ object has no attribute ‘column_index’

import numpy as np from pymatgen.io.vasp import Vasprun from tqdm import tqdm import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import os import matplotlib as mpl 设置全局绘图参数 mpl.rcParams[‘font.family’] = ‘serif’ # 期刊常用字体 mpl.rcParams[‘mathtext.fontset’] = ‘dejavuserif’ # 数学字体 mpl.rcParams[‘axes.linewidth’] = 1.5 # 增加轴线宽度 mpl.rcParams[‘lines.linewidth’] = 2.5 # 线宽 mpl.rcParams[‘figure.dpi’] = 300 # 高分辨率 def get_angle(A, B, C): “”“计算三个点之间的角度(B为顶点)”“” BA = A - B BC = C - B cos_theta = np.dot(BA, BC) / (np.linalg.norm(BA) * np.linalg.norm(BC)) cos_theta = np.clip(cos_theta, -1.0, 1.0) # 避免数值误差 return np.degrees(np.arccos(cos_theta)) def analyze_system(system_name, vasprun_file, output_dir): “”“分析单个体系的氢键分布”“” # 创建体系特定输出目录 system_dir = os.path.join(output_dir, system_name) os.makedirs(system_dir, exist_ok=True) # 初始化分类存储 bond_types = { 'O-O': {'angles': [], 'color': '#1f77b4'}, 'O-F': {'angles': [], 'color': '#ff7f0e'}, 'F-O': {'angles': [], 'color': '#2ca02c'}, 'F-F': {'angles': [], 'color': '#d62728'} } # 参数设置 (可根据不同体系调整) step = 100 # 采样步长 donor_cutoff = 1.2 # 供体原子最大距离 (Å) receptor_cutoff = 2.5 # 受体原子最大距离 (Å) angle_threshold = 120 # 最小氢键角度 (度) print(f"[{system_name}] 正在读取 {vasprun_file} 文件...") try: vr = Vasprun(vasprun_file) except Exception as e: print(f"[{system_name}] 读取vasprun文件时出错: {e}") return system_name, bond_types # 返回空结果 structures = vr.structures print(f"[{system_name}] 总帧数: {len(structures)}, 采样步长: {step}") # 主分析循环 for frame_idx, structure in enumerate(tqdm(structures[::step], desc=f"{system_name}氢键分析", unit="帧")): # 预处理原子索引(优化性能) o_f_sites = [site for site in structure if site.species_string in ("O", "F")] for H_site in structure: if H_site.species_string != "H": continue # 寻找供体原子 donor = min( (site for site in o_f_sites), key=lambda x: H_site.distance(x), default=None ) if donor is None or H_site.distance(donor) > donor_cutoff: continue # 获取受体原子 receptors = [] for neighbor in structure.get_neighbors(H_site, r=receptor_cutoff): try: site = neighbor.site dist = neighbor.distance except AttributeError: site, dist, _, _ = neighbor if (site.species_string in ("O", "F") and site != donor and dist <= receptor_cutoff): receptors.append(site) # 分类计算角度 for receptor in receptors: angle = get_angle(donor.coords, H_site.coords, receptor.coords) if angle < angle_threshold: continue # 确定键类型 donor_type = donor.species_string receptor_type = receptor.species_string bond_key = f"{donor_type}-{receptor_type}" if bond_key in bond_types: bond_types[bond_key]['angles'].append(angle) # 保存体系统计数据 stats_data = [] for bond_type, data in bond_types.items(): angles = data['angles'] if len(angles) == 0: continue stats_data.append({ 'Type': bond_type, 'Count': len(angles), 'Mean': np.mean(angles), 'Std': np.std(angles), 'Median': np.median(angles), 'Min': np.min(angles), 'Max': np.max(angles) }) stats_df = pd.DataFrame(stats_data) stats_file = os.path.join(system_dir, "bond_type_stats.csv") stats_df.to_csv(stats_file, index=False) print(f"[{system_name}] 键型统计已保存到 {stats_file}") # 保存当前体系的分布图 plot_single_system(system_name, bond_types, system_dir) return system_name, bond_types def plot_single_system(system_name, bond_types, output_dir): “”“为单个体系绘制分布图并保存”“” plt.figure(figsize=(10, 6)) sns.set_style(“whitegrid”) # 统一设置颜色映射 bond_colors = { 'O-O': '#1f77b4', 'O-F': '#ff7f0e', 'F-O': '#2ca02c', 'F-F': '#d62728' } bins = np.linspace(90, 180, 30) # 绘制分布 for bond_type, data in bond_types.items(): angles = data['angles'] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type} (n={len(angles)})", color=bond_colors[bond_type], linewidth=2.5, alpha=0.8) # 直方图叠加 plt.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置图表属性 plt.xlabel("Hydrogen Bond Angle (degrees)", fontsize=14) plt.ylabel("Probability Density", fontsize=14) plt.title(f"Hydrogen Bond Angle Distribution: {system_name}", fontsize=16) plt.xlim(90, 180) plt.ylim(0, 0.035) plt.legend(title="Bond Type", fontsize=12) # 保存图像 plot_file = os.path.join(output_dir, f"{system_name}_bond_distribution.png") plt.tight_layout() plt.savefig(plot_file, dpi=300, bbox_inches='tight') plt.close() # 关闭图形释放内存 print(f"[{system_name}] 已保存分布图到 {plot_file}") def plot_combined_results(results, output_dir): “”“将四个体系的结果绘制在统一坐标的子图中并保存”“” plt.figure(figsize=(14, 12)) sns.set(style=“whitegrid”, font_scale=1.2, palette=“muted”) bins = np.linspace(90, 180, 30) # 创建2x2子图 fig, axes = plt.subplots(2, 2, figsize=(14, 12), sharex=True, sharey=True) axes = axes.flatten() # 统一设置颜色映射 bond_colors = { 'O-O': '#1f77b4', 'O-F': '#ff7f0e', 'F-O': '#2ca02c', 'F-F': '#d62728' } # 为每个体系绘制子图 for idx, (system_name, bond_types) in enumerate(results.items()): ax = axes[idx] # 绘制每种键型的分布 for bond_type, data in bond_types.items(): angles = data['angles'] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type}", color=bond_colors[bond_type], linewidth=2.5, ax=ax) # 直方图叠加 ax.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置子图标题和标签 ax.set_title(f"{system_name}", fontsize=14, fontweight='bold') ax.set_xlabel("Hydrogen Bond Angle (degrees)", fontsize=12) ax.set_ylabel("Probability Density", fontsize=12) ax.set_xlim(90, 180) ax.set_ylim(0, 0.035) # 统一Y轴范围 ax.legend(title="Bond Type", fontsize=10, loc='upper left') ax.grid(True, linestyle='--', alpha=0.7) # 添加统计信息文本框 stats_text = "\n".join([f"{bt}: n={len(data['angles'])}" for bt, data in bond_types.items() if len(data['angles']) > 0]) ax.text(0.95, 0.95, stats_text, transform=ax.transAxes, verticalalignment='top', horizontalalignment='right', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8), fontsize=9) # 添加整体标题 plt.suptitle("Hydrogen Bond Angle Distribution in Different Systems", fontsize=16, fontweight='bold', y=0.98) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.96]) # 保存组合图 combined_file = os.path.join(output_dir, "combined_bond_distribution.png") plt.savefig(combined_file, dpi=300, bbox_inches='tight') plt.close() # 关闭图形释放内存 print(f"已保存组合分布图到 {combined_file}") # 保存组合图PDF版本(期刊投稿要求) combined_pdf = os.path.join(output_dir, "combined_bond_distribution.pdf") plt.savefig(combined_pdf, bbox_inches='tight') print(f"已保存组合分布图PDF版本到 {combined_pdf}") if name == “main”: # ===== 配置四个体系 ===== # 假设所有vasprun文件都在当前目录下 systems = { “System_1”: “vasprun1.xml”, “System_2”: “vasprun2.xml”, “System_3”: “vasprun3.xml”, “System_4”: “vasprun4.xml” } output_dir = "hydrogen_bond_analysis" os.makedirs(output_dir, exist_ok=True) # ===== 顺序处理四个体系 ===== results = {} for system_name, file in tqdm(systems.items(), desc="处理体系"): try: system_name, bond_types = analyze_system(system_name, file, output_dir) results[system_name] = bond_types except Exception as e: print(f"处理体系 {system_name} 时出错: {e}") # ===== 绘制组合图 ===== plot_combined_results(results, output_dir) print("\n所有体系分析完成!结果保存在", output_dir) print("包含:") print(" - 每个体系的单独目录(包含统计数据和分布图)") print(" - 组合分布图 (combined_bond_distribution.png 和 .pdf)")以上代码实现了对四个vasprun体系中计算四类氢键键角的角度分布情况,在这里我们对其中的原子分类逻辑做进一步优化,对计算的类别进一步优化,然后将该代码转为在58核100g内存的台式ananconda promt中执行,先识别P,在P周围搜寻O原子,如果该O原子距离在1.6埃以内则视为Op,而对于Op在其周围搜寻H原子,如果该H在距离1.3埃以内则视为成键即该H为Hp,通过是否有Hp则可以识别P-OH与P=O/P-O,在这里P=O和P-O还不能区分,在这里我们将不对P=O进行区分识别,为方便清晰,将两者统一记为P-O/P=O。接着体系中全部的O原子在去除Op之后剩下的O,在这些剩余的O周围搜寻整体的H,如果H的距离在1.2埃以内则视为成键,然后依照成键的H数量判定:如果H的数量为1,则记为-OH羟基(在这里不需要计算羟基部分,只是识别出来有利于逻辑完整性,并不参与RDF计算,也不需要特别标注表明),H的数量为2,则记为H2O水(该O也随之记为Ow,对应的两个H也记为Hw),如果H的数量为3,则记为水合氢离子(该O随之记为Oh,对应的三个H也记为Hh)。体系中存在质子转移的情况,所以需要每一帧重新识别原子的归属问题,如果H同时处于两个成键识别范围则按照就近原则,离哪个近则归属到哪一个(这里包括磷酸-磷酸,磷酸-水,磷酸-水合氢离子,水-水,水-水合氢离子,水合氢离子-水合氢离子,如果H同时处于某种情况下两个化学成键范围则采用就近原则),在实时重新归属质子的情况下,计算出包含质子转移部分的RDF,在这里,我们将排除自身化学键的阈值先设置为0,不需要只看氢键部分了。直接将-OH视为质子转移或者不完整而直接忽略即可,磷酸上的O需要通过H来进一步识别,所以符合距离的氧可暂时作为Op的候选,等H的识别完成再进行细分P=O/P-O和P-OH。同时由于后续RDF的计算过程中我们发现,在这里我们只需要将原先对四种类别的计算改为对九类的计算,分别为P-O/P=O···Hw P-O/P=O···Hh P-O/P=O···Hp P-OH···Ow Hw···Ow Hh···Ow Hw···F Hh···F Hp···F,以上补充的逻辑中提供了化学键的判别(供体和H之间的距离),在这里我们对H和受体之间的距离再进一步补充,这九类分别的距离分别为2.375 2.275 2.175 2.275 2.375 2.225 2.225 2.175 2.275.这数据的导出需要能导入origin中方便我后续作图,在原代码中文件名后加上2,作为第二版的意思。 回答 向我提问的人太多了。正在努力扩容中,请稍后再试。 import numpy as np from pymatgen.io.vasp import Vasprun from tqdm import tqdm import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import os import matplotlib as mpl 设置全局绘图参数 mpl.rcParams[‘font.family’] = ‘serif’ # 期刊常用字体 mpl.rcParams[‘mathtext.fontset’] = ‘dejavuserif’ # 数学字体 mpl.rcParams[‘axes.linewidth’] = 1.5 # 增加轴线宽度 mpl.rcParams[‘lines.linewidth’] = 2.5 # 线宽 mpl.rcParams[‘figure.dpi’] = 300 # 高分辨率 def get_angle(A, B, C): “”“计算三个点之间的角度(B为顶点)”“” BA = A - B BC = C - B cos_theta = np.dot(BA, BC) / (np.linalg.norm(BA) * np.linalg.norm(BC)) cos_theta = np.clip(cos_theta, -1.0, 1.0) # 避免数值误差 return np.degrees(np.arccos(cos_theta)) def analyze_system(system_name, vasprun_file, output_dir): “”“分析单个体系的氢键分布”“” 创建体系特定输出目录 system_dir = os.path.join(output_dir, system_name) os.makedirs(system_dir, exist_ok=True) 初始化分类存储 bond_types = { ‘O-O’: {‘angles’: [], ‘color’: ‘#1f77b4’}, ‘O-F’: {‘angles’: [], ‘color’: ‘#ff7f0e’}, ‘F-O’: {‘angles’: [], ‘color’: ‘#2ca02c’}, ‘F-F’: {‘angles’: [], ‘color’: ‘#d62728’} } # 参数设置 (可根据不同体系调整) step = 100 # 采样步长 donor_cutoff = 1.2 # 供体原子最大距离 (Å) receptor_cutoff = 2.5 # 受体原子最大距离 (Å) angle_threshold = 120 # 最小氢键角度 (度) print(f"[{system_name}] 正在读取 {vasprun_file} 文件…“) try: vr = Vasprun(vasprun_file) except Exception as e: print(f”[{system_name}] 读取vasprun文件时出错: {e}“) return system_name, bond_types # 返回空结果 structures = vr.structures print(f”[{system_name}] 总帧数: {len(structures)}, 采样步长: {step}“) # 主分析循环 for frame_idx, structure in enumerate(tqdm(structures[::step], desc=f”{system_name}氢键分析", unit=“帧”)): # 预处理原子索引(优化性能) o_f_sites = [site for site in structure if site.species_string in (“O”, “F”)] for H_site in structure: if H_site.species_string != “H”: continue # 寻找供体原子 donor = min( (site for site in o_f_sites), key=lambda x: H_site.distance(x), default=None ) if donor is None or H_site.distance(donor) > donor_cutoff: continue # 获取受体原子 receptors = [] for neighbor in structure.get_neighbors(H_site, r=receptor_cutoff): try: site = neighbor.site dist = neighbor.distance except AttributeError: site, dist, _, _ = neighbor if (site.species_string in (“O”, “F”) and site != donor and dist <= receptor_cutoff): receptors.append(site) # 分类计算角度 for receptor in receptors: angle = get_angle(donor.coords, H_site.coords, receptor.coords) if angle < angle_threshold: continue # 确定键类型 donor_type = donor.species_string receptor_type = receptor.species_string bond_key = f"{donor_type}-{receptor_type}" if bond_key in bond_types: bond_types[bond_key][‘angles’].append(angle) # 保存体系统计数据 stats_data = [] for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue stats_data.append({ ‘Type’: bond_type, ‘Count’: len(angles), ‘Mean’: np.mean(angles), ‘Std’: np.std(angles), ‘Median’: np.median(angles), ‘Min’: np.min(angles), ‘Max’: np.max(angles) }) stats_df = pd.DataFrame(stats_data) stats_file = os.path.join(system_dir, “bond_type_stats.csv”) stats_df.to_csv(stats_file, index=False) print(f"[{system_name}] 键型统计已保存到 {stats_file}") # 保存当前体系的分布图 plot_single_system(system_name, bond_types, system_dir) return system_name, bond_types def plot_single_system(system_name, bond_types, output_dir): “”“为单个体系绘制分布图并保存”“” plt.figure(figsize=(10, 6)) sns.set_style(“whitegrid”) 统一设置颜色映射 bond_colors = { ‘O-O’: ‘#1f77b4’, ‘O-F’: ‘#ff7f0e’, ‘F-O’: ‘#2ca02c’, ‘F-F’: ‘#d62728’ } bins = np.linspace(90, 180, 30) # 绘制分布 for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type} (n={len(angles)})“, color=bond_colors[bond_type], linewidth=2.5, alpha=0.8) # 直方图叠加 plt.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置图表属性 plt.xlabel(“Hydrogen Bond Angle (degrees)”, fontsize=14) plt.ylabel(“Probability Density”, fontsize=14) plt.title(f"Hydrogen Bond Angle Distribution: {system_name}”, fontsize=16) plt.xlim(90, 180) plt.ylim(0, 0.035) plt.legend(title=“Bond Type”, fontsize=12) # 保存图像 plot_file = os.path.join(output_dir, f"{system_name}_bond_distribution.png") plt.tight_layout() plt.savefig(plot_file, dpi=300, bbox_inches=‘tight’) plt.close() # 关闭图形释放内存 print(f"[{system_name}] 已保存分布图到 {plot_file}") def plot_combined_results(results, output_dir): “”“将四个体系的结果绘制在统一坐标的子图中并保存”“” plt.figure(figsize=(14, 12)) sns.set(style=“whitegrid”, font_scale=1.2, palette=“muted”) bins = np.linspace(90, 180, 30) 创建2x2子图 fig, axes = plt.subplots(2, 2, figsize=(14, 12), sharex=True, sharey=True) axes = axes.flatten() # 统一设置颜色映射 bond_colors = { ‘O-O’: ‘#1f77b4’, ‘O-F’: ‘#ff7f0e’, ‘F-O’: ‘#2ca02c’, ‘F-F’: ‘#d62728’ } # 为每个体系绘制子图 for idx, (system_name, bond_types) in enumerate(results.items()): ax = axes[idx] # 绘制每种键型的分布 for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type}“, color=bond_colors[bond_type], linewidth=2.5, ax=ax) # 直方图叠加 ax.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置子图标题和标签 ax.set_title(f”{system_name}“, fontsize=14, fontweight=‘bold’) ax.set_xlabel(“Hydrogen Bond Angle (degrees)”, fontsize=12) ax.set_ylabel(“Probability Density”, fontsize=12) ax.set_xlim(90, 180) ax.set_ylim(0, 0.035) # 统一Y轴范围 ax.legend(title=“Bond Type”, fontsize=10, loc=‘upper left’) ax.grid(True, linestyle=‘–’, alpha=0.7) # 添加统计信息文本框 stats_text = “\n”.join([f”{bt}: n={len(data[‘angles’])}" for bt, data in bond_types.items() if len(data[‘angles’]) > 0]) ax.text(0.95, 0.95, stats_text, transform=ax.transAxes, verticalalignment=‘top’, horizontalalignment=‘right’, bbox=dict(boxstyle=‘round’, facecolor=‘white’, alpha=0.8), fontsize=9) # 添加整体标题 plt.suptitle(“Hydrogen Bond Angle Distribution in Different Systems”, fontsize=16, fontweight=‘bold’, y=0.98) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.96]) # 保存组合图 combined_file = os.path.join(output_dir, “combined_bond_distribution.png”) plt.savefig(combined_file, dpi=300, bbox_inches=‘tight’) plt.close() # 关闭图形释放内存 print(f"已保存组合分布图到 {combined_file}“) # 保存组合图PDF版本(期刊投稿要求) combined_pdf = os.path.join(output_dir, “combined_bond_distribution.pdf”) plt.savefig(combined_pdf, bbox_inches=‘tight’) print(f"已保存组合分布图PDF版本到 {combined_pdf}”) if name == “main”: ===== 配置四个体系 ===== 假设所有vasprun文件都在当前目录下 systems = { “System_1”: “vasprun1.xml”, “System_2”: “vasprun2.xml”, “System_3”: “vasprun3.xml”, “System_4”: “vasprun4.xml” } output_dir = “hydrogen_bond_analysis” os.makedirs(output_dir, exist_ok=True) # ===== 顺序处理四个体系 ===== results = {} for system_name, file in tqdm(systems.items(), desc=“处理体系”): try: system_name, bond_types = analyze_system(system_name, file, output_dir) results[system_name] = bond_types except Exception as e: print(f"处理体系 {system_name} 时出错: {e}“) # ===== 绘制组合图 ===== plot_combined_results(results, output_dir) print(”\n所有体系分析完成!结果保存在", output_dir) print(“包含:”) print(" - 每个体系的单独目录(包含统计数据和分布图)“) print(” - 组合分布图 (combined_bond_distribution.png 和 .pdf)")以上代码实现了对四个vasprun体系中计算四类氢键键角的角度分布情况,在这里我们对其中的原子分类逻辑做进一步优化,对计算的类别进一步优化,然后将该代码转为在58核100g内存的台式ananconda promt中执行,先识别P,在P周围搜寻O原子,如果该O原子距离在1.6埃以内则视为Op,而对于Op在其周围搜寻H原子,如果该H在距离1.3埃以内则视为成键即该H为Hp,通过是否有Hp则可以识别P-OH与P=O/P-O,在这里P=O和P-O还不能区分,在这里我们将不对P=O进行区分识别,为方便清晰,将两者统一记为P-O/P=O。接着体系中全部的O原子在去除Op之后剩下的O,在这些剩余的O周围搜寻整体的H,如果H的距离在1.2埃以内则视为成键,然后依照成键的H数量判定:如果H的数量为1,则记为-OH羟基(在这里不需要计算羟基部分,只是识别出来有利于逻辑完整性,并不参与RDF计算,也不需要特别标注表明),H的数量为2,则记为H2O水(该O也随之记为Ow,对应的两个H也记为Hw),如果H的数量为3,则记为水合氢离子(该O随之记为Oh,对应的三个H也记为Hh)。体系中存在质子转移的情况,所以需要每一帧重新识别原子的归属问题,如果H同时处于两个成键识别范围则按照就近原则,离哪个近则归属到哪一个(这里包括磷酸-磷酸,磷酸-水,磷酸-水合氢离子,水-水,水-水合氢离子,水合氢离子-水合氢离子,如果H同时处于某种情况下两个化学成键范围则采用就近原则),在实时重新归属质子的情况下,计算出包含质子转移部分的RDF,在这里,我们将排除自身化学键的阈值先设置为0,不需要只看氢键部分了。直接将-OH视为质子转移或者不完整而直接忽略即可,磷酸上的O需要通过H来进一步识别,所以符合距离的氧可暂时作为Op的候选,等H的识别完成再进行细分P=O/P-O和P-OH。同时由于后续RDF的计算过程中我们发现,在这里我们只需要将原先对四种类别的计算改为对九类的计算,分别为P-O/P=O···Hw P-O/P=O···Hh P-O/P=O···Hp P-OH···Ow Hw···Ow Hh···Ow Hw···F Hh···F Hp···F,以上补充的逻辑中提供了化学键的判别(供体和H之间的距离),在这里我们对H和受体之间的距离再进一步补充,这九类分别的距离分别为2.375 2.275 2.175 2.275 2.375 2.225 2.225 2.175 2.275.这数据的导出需要能导入origin中方便我后续作图,在原代码中文件名后加上2,作为第二版的意思。 回答 向我提问的人太多了。正在努力扩容中,请稍后再试。 import numpy as np from pymatgen.io.vasp import Vasprun from tqdm import tqdm import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import os import matplotlib as mpl 设置全局绘图参数 mpl.rcParams[‘font.family’] = ‘serif’ # 期刊常用字体 mpl.rcParams[‘mathtext.fontset’] = ‘dejavuserif’ # 数学字体 mpl.rcParams[‘axes.linewidth’] = 1.5 # 增加轴线宽度 mpl.rcParams[‘lines.linewidth’] = 2.5 # 线宽 mpl.rcParams[‘figure.dpi’] = 300 # 高分辨率 def get_angle(A, B, C): “”“计算三个点之间的角度(B为顶点)”“” BA = A - B BC = C - B cos_theta = np.dot(BA, BC) / (np.linalg.norm(BA) * np.linalg.norm(BC)) cos_theta = np.clip(cos_theta, -1.0, 1.0) # 避免数值误差 return np.degrees(np.arccos(cos_theta)) def analyze_system(system_name, vasprun_file, output_dir): “”“分析单个体系的氢键分布”“” 创建体系特定输出目录 system_dir = os.path.join(output_dir, system_name) os.makedirs(system_dir, exist_ok=True) 初始化分类存储 bond_types = { ‘O-O’: {‘angles’: [], ‘color’: ‘#1f77b4’}, ‘O-F’: {‘angles’: [], ‘color’: ‘#ff7f0e’}, ‘F-O’: {‘angles’: [], ‘color’: ‘#2ca02c’}, ‘F-F’: {‘angles’: [], ‘color’: ‘#d62728’} } # 参数设置 (可根据不同体系调整) step = 100 # 采样步长 donor_cutoff = 1.2 # 供体原子最大距离 (Å) receptor_cutoff = 2.5 # 受体原子最大距离 (Å) angle_threshold = 120 # 最小氢键角度 (度) print(f"[{system_name}] 正在读取 {vasprun_file} 文件…“) try: vr = Vasprun(vasprun_file) except Exception as e: print(f”[{system_name}] 读取vasprun文件时出错: {e}“) return system_name, bond_types # 返回空结果 structures = vr.structures print(f”[{system_name}] 总帧数: {len(structures)}, 采样步长: {step}“) # 主分析循环 for frame_idx, structure in enumerate(tqdm(structures[::step], desc=f”{system_name}氢键分析", unit=“帧”)): # 预处理原子索引(优化性能) o_f_sites = [site for site in structure if site.species_string in (“O”, “F”)] for H_site in structure: if H_site.species_string != “H”: continue # 寻找供体原子 donor = min( (site for site in o_f_sites), key=lambda x: H_site.distance(x), default=None ) if donor is None or H_site.distance(donor) > donor_cutoff: continue # 获取受体原子 receptors = [] for neighbor in structure.get_neighbors(H_site, r=receptor_cutoff): try: site = neighbor.site dist = neighbor.distance except AttributeError: site, dist, _, _ = neighbor if (site.species_string in (“O”, “F”) and site != donor and dist <= receptor_cutoff): receptors.append(site) # 分类计算角度 for receptor in receptors: angle = get_angle(donor.coords, H_site.coords, receptor.coords) if angle < angle_threshold: continue # 确定键类型 donor_type = donor.species_string receptor_type = receptor.species_string bond_key = f"{donor_type}-{receptor_type}" if bond_key in bond_types: bond_types[bond_key][‘angles’].append(angle) # 保存体系统计数据 stats_data = [] for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue stats_data.append({ ‘Type’: bond_type, ‘Count’: len(angles), ‘Mean’: np.mean(angles), ‘Std’: np.std(angles), ‘Median’: np.median(angles), ‘Min’: np.min(angles), ‘Max’: np.max(angles) }) stats_df = pd.DataFrame(stats_data) stats_file = os.path.join(system_dir, “bond_type_stats.csv”) stats_df.to_csv(stats_file, index=False) print(f"[{system_name}] 键型统计已保存到 {stats_file}") # 保存当前体系的分布图 plot_single_system(system_name, bond_types, system_dir) return system_name, bond_types def plot_single_system(system_name, bond_types, output_dir): “”“为单个体系绘制分布图并保存”“” plt.figure(figsize=(10, 6)) sns.set_style(“whitegrid”) 统一设置颜色映射 bond_colors = { ‘O-O’: ‘#1f77b4’, ‘O-F’: ‘#ff7f0e’, ‘F-O’: ‘#2ca02c’, ‘F-F’: ‘#d62728’ } bins = np.linspace(90, 180, 30) # 绘制分布 for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type} (n={len(angles)})“, color=bond_colors[bond_type], linewidth=2.5, alpha=0.8) # 直方图叠加 plt.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置图表属性 plt.xlabel(“Hydrogen Bond Angle (degrees)”, fontsize=14) plt.ylabel(“Probability Density”, fontsize=14) plt.title(f"Hydrogen Bond Angle Distribution: {system_name}”, fontsize=16) plt.xlim(90, 180) plt.ylim(0, 0.035) plt.legend(title=“Bond Type”, fontsize=12) # 保存图像 plot_file = os.path.join(output_dir, f"{system_name}_bond_distribution.png") plt.tight_layout() plt.savefig(plot_file, dpi=300, bbox_inches=‘tight’) plt.close() # 关闭图形释放内存 print(f"[{system_name}] 已保存分布图到 {plot_file}") def plot_combined_results(results, output_dir): “”“将四个体系的结果绘制在统一坐标的子图中并保存”“” plt.figure(figsize=(14, 12)) sns.set(style=“whitegrid”, font_scale=1.2, palette=“muted”) bins = np.linspace(90, 180, 30) 创建2x2子图 fig, axes = plt.subplots(2, 2, figsize=(14, 12), sharex=True, sharey=True) axes = axes.flatten() # 统一设置颜色映射 bond_colors = { ‘O-O’: ‘#1f77b4’, ‘O-F’: ‘#ff7f0e’, ‘F-O’: ‘#2ca02c’, ‘F-F’: ‘#d62728’ } # 为每个体系绘制子图 for idx, (system_name, bond_types) in enumerate(results.items()): ax = axes[idx] # 绘制每种键型的分布 for bond_type, data in bond_types.items(): angles = data[‘angles’] if len(angles) == 0: continue # KDE曲线 sns.kdeplot(angles, label=f"{bond_type}“, color=bond_colors[bond_type], linewidth=2.5, ax=ax) # 直方图叠加 ax.hist(angles, bins=bins, color=bond_colors[bond_type], alpha=0.2, density=True) # 设置子图标题和标签 ax.set_title(f”{system_name}“, fontsize=14, fontweight=‘bold’) ax.set_xlabel(“Hydrogen Bond Angle (degrees)”, fontsize=12) ax.set_ylabel(“Probability Density”, fontsize=12) ax.set_xlim(90, 180) ax.set_ylim(0, 0.035) # 统一Y轴范围 ax.legend(title=“Bond Type”, fontsize=10, loc=‘upper left’) ax.grid(True, linestyle=‘–’, alpha=0.7) # 添加统计信息文本框 stats_text = “\n”.join([f”{bt}: n={len(data[‘angles’])}" for bt, data in bond_types.items() if len(data[‘angles’]) > 0]) ax.text(0.95, 0.95, stats_text, transform=ax.transAxes, verticalalignment=‘top’, horizontalalignment=‘right’, bbox=dict(boxstyle=‘round’, facecolor=‘white’, alpha=0.8), fontsize=9) # 添加整体标题 plt.suptitle(“Hydrogen Bond Angle Distribution in Different Systems”, fontsize=16, fontweight=‘bold’, y=0.98) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.96]) # 保存组合图 combined_file = os.path.join(output_dir, “combined_bond_distribution.png”) plt.savefig(combined_file, dpi=300, bbox_inches=‘tight’) plt.close() # 关闭图形释放内存 print(f"已保存组合分布图到 {combined_file}“) # 保存组合图PDF版本(期刊投稿要求) combined_pdf = os.path.join(output_dir, “combined_bond_distribution.pdf”) plt.savefig(combined_pdf, bbox_inches=‘tight’) print(f"已保存组合分布图PDF版本到 {combined_pdf}”) if name == “main”: ===== 配置四个体系 ===== 假设所有vasprun文件都在当前目录下 systems = { “System_1”: “vasprun1.xml”, “System_2”: “vasprun2.xml”, “System_3”: “vasprun3.xml”, “System_4”: “vasprun4.xml” } output_dir = “hydrogen_bond_analysis” os.makedirs(output_dir, exist_ok=True) # ===== 顺序处理四个体系 ===== results = {} for system_name, file in tqdm(systems.items(), desc=“处理体系”): try: system_name, bond_types = analyze_system(system_name, file, output_dir) results[system_name] = bond_types except Exception as e: print(f"处理体系 {system_name} 时出错: {e}“) # ===== 绘制组合图 ===== plot_combined_results(results, output_dir) print(”\n所有体系分析完成!结果保存在", output_dir) print(“包含:”) print(" - 每个体系的单独目录(包含统计数据和分布图)“) print(” - 组合分布图 (combined_bond_distribution.png 和 .pdf)")以上代码实现了对四个vasprun体系中计算四类氢键键角的角度分布情况,在这里我们对其中的原子分类逻辑做进一步优化,对计算的类别进一步优化,然后将该代码转为在58核100g内存的台式ananconda promt中执行,先识别P,在P周围搜寻O原子,如果该O原子距离在1.6埃以内则视为Op,而对于Op在其周围搜寻H原子,如果该H在距离1.3埃以内则视为成键即该H为Hp,通过是否有Hp则可以识别P-OH与P=O/P-O,在这里P=O和P-O还不能区分,在这里我们将不对P=O进行区分识别,为方便清晰,将两者统一记为P-O/P=O。接着体系中全部的O原子在去除Op之后剩下的O,在这些剩余的O周围搜寻整体的H,如果H的距离在1.2埃以内则视为成键,然后依照成键的H数量判定:如果H的数量为1,则记为-OH羟基(在这里不需要计算羟基部分,只是识别出来有利于逻辑完整性,并不参与RDF计算,也不需要特别标注表明),H的数量为2,则记为H2O水(该O也随之记为Ow,对应的两个H也记为Hw),如果H的数量为3,则记为水合氢离子(该O随之记为Oh,对应的三个H也记为Hh)。体系中存在质子转移的情况,所以需要每一帧重新识别原子的归属问题,如果H同时处于两个成键识别范围则按照就近原则,离哪个近则归属到哪一个(这里包括磷酸-磷酸,磷酸-水,磷酸-水合氢离子,水-水,水-水合氢离子,水合氢离子-水合氢离子,如果H同时处于某种情况下两个化学成键范围则采用就近原则),在实时重新归属质子的情况下,计算出包含质子转移部分的RDF,在这里,我们将排除自身化学键的阈值先设置为0,不需要只看氢键部分了。直接将-OH视为质子转移或者不完整而直接忽略即可,磷酸上的O需要通过H来进一步识别,所以符合距离的氧可暂时作为Op的候选,等H的识别完成再进行细分P=O/P-O和P-OH。同时由于后续RDF的计算过程中我们发现,在这里我们只需要将原先对四种类别的计算改为对九类的计算,分别为P-O/P=O···Hw P-O/P=O···Hh P-O/P=O···Hp P-OH···Ow Hw···Ow Hh···Ow Hw···F Hh···F Hp···F,以上补充的逻辑中提供了化学键的判别(供体和H之间的距离),在这里我们对H和受体之间的距离再进一步补充,这九类分别的距离分别为2.375 2.275 2.175 2.275 2.375 2.225 2.225 2.175 2.275.这数据的导出需要能导入origin中方便我后续作图,在原代码中文件名后加上2,作为第二版的意思。其中控制核数在58左右,内存控制在100g左右。

import oml import xml.etree.ElementTree as ET from datetime import datetime, timedelta import re import numpy as np import pandas as pd import warnings import os import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler import seaborn as sns import matplotlib.dates as mdates warnings.filterwarnings('ignore') PIC_DIR = "pic" if not os.path.exists(PIC_DIR): os.makedirs(PIC_DIR) def format_timestamp(ts): """格式化时间戳为Oracle可接受的字符串""" return ts.strftime("%Y-%m-%d %H:%M:%S.%f")[:23] def is_holiday(date): """判断日期是否为休息日(简化实现)""" # 在实际应用中,这里应该连接到日历表或API # 周末判断(周六=5,周日=6) if date.weekday() in [5, 6]: return True # 这里可以添加特定假期的判断 # 例如:if date.month == 1 and date.day == 1: return True return False def find_previous_workday(target_date): """找到前一个工作日""" current = target_date - timedelta(days=1) while is_holiday(current): current -= timedelta(days=1) return current def calculate_std_dev(df, exclude_columns): """计算数据框的标准差(排除指定列)""" # 排除不需要的列 numeric_cols = [col for col in df.columns if col not in exclude_columns] if not numeric_cols: return 0, {} # 计算每列的标准差 col_std = {} for col in numeric_cols: if df[col].dtype in [np.int64, np.float64]: col_std[col] = df[col].std() # 计算所有数值列合并后的标准差 all_values = [] for col in numeric_cols: if df[col].dtype in [np.int64, np.float64]: all_values.extend(df[col].values) total_std = np.std(all_values) if all_values else 0 return total_std, col_std def get_relative_change(current, reference): """计算相对变化率,处理除零问题""" if reference == 0 and current == 0: return 0 # 两者都为零,无变化 elif reference == 0: return float('inf') # 参考值为零,当前值非零 else: return abs(current - reference) / reference * 100 def classify_change(change_rate): """分类变化率,处理无穷大情况""" if change_rate == float('inf'): return "极端变化(参考值为零)", "m4" elif change_rate < 5: return "正常波动(可忽略)", "m1" elif 5 <= change_rate < 10: return "值得关注的变化", "m2" elif 10 <= change_rate < 20: return "显著变化", "m3" else: return "重大变化", "m4" def format_change_rate(change_rate): """格式化变化率显示""" if change_rate == float('inf'): return "无穷大" else: return f"{change_rate:.2f}%" def get_data_samples(cursor, obj_name, inst_number, target_time, num_samples=10): """获取目标时间点之前的样本数据,处理标准差为零的情况""" # 获取目标时间点之前的10个采样点 query = f""" SELECT * FROM "{obj_name}" WHERE instance_number = {inst_number} AND snap_time < TO_TIMESTAMP('{format_timestamp(target_time)}', 'YYYY-MM-DD HH24:MI:SS.FF3') ORDER BY snap_time DESC FETCH FIRST {num_samples} ROWS ONLY """ cursor.execute(query) result = cursor.fetchall() if not result: return None, None, None # 获取列名 col_names = [col[0] for col in cursor.description] # 创建DataFrame df = pd.DataFrame(result, columns=col_names) # 排除不需要的列 exclude_cols = ['SNAP_TIME', 'INSTANCE_NUMBER', 'PRED', 'PROB', 'ANOMALYDETAILS'] # 计算标准差 total_std, col_std = calculate_std_dev(df, exclude_cols) # 如果标准差为零,添加微小噪声避免除零错误 if total_std == 0: total_std = 1e-6 for col in col_std: if col_std[col] == 0: col_std[col] = 1e-6 return df, total_std, col_std def generate_description(attr): """生成指标问题描述""" rank = attr['rank'] weight = attr['weight'] name = attr['name'] if rank == 1: return f"主要异常指标({weight*100:.0f}%权重),直接影响系统稳定性" elif weight > 0.7: return f"关键异常指标({weight*100:.0f}%权重),需要立即处理" elif weight > 0.3: return f"重要异常指标({weight*100:.0f}%权重),建议优先排查" else: return f"参考指标({weight*100:.0f}%权重),可作为辅助分析" def escape_sql(text): """转义SQL中的特殊字符""" return text.replace("'", "''").replace("\n", " ") def analyze_anomalies(): cursor = None conn = None obj_name, anom_time, anom_inst = None, None, None pic_paths = [] # 存储生成的图片路径 m4_indicators = [] # 存储m4级别的指标名称 try: # 1. 连接数据库 conn = oml.connect(user='aiopsoraoem', password='oracle', host='localhost', port=1521, service_name='aiopspdb') print("数据库连接成功") # 创建游标对象用于执行SQL cursor = oml.cursor() # 2. 获取待分析的异常记录 query = """ SELECT object_name, anomalies_time, anomalies_inst_number FROM RT_RESULT WHERE alert_analysis_status IS NULL AND anomalies_time > SYSDATE - 10/1440 ORDER BY anomalies_time DESC FETCH FIRST 1 ROWS ONLY """ # 执行查询 cursor.execute(query) result = cursor.fetchall() if not result: print("没有需要分析的异常记录") return # 提取数据 obj_name = result[0][0] anom_time = result[0][1] anom_inst = int(result[0][2]) print(f"分析记录: {obj_name} at {anom_time} (实例 {anom_inst})") # 3. 更新状态为doing update_query = f""" UPDATE RT_RESULT SET alert_analysis_status = 'doing' WHERE object_name = '{escape_sql(obj_name)}' AND anomalies_time = TO_TIMESTAMP('{format_timestamp(anom_time)}', 'YYYY-MM-DD HH24:MI:SS.FF3') AND anomalies_inst_number = {anom_inst} """ cursor.execute(update_query) cursor.connection.commit() print("状态更新为doing") # 4. 获取异常详情 detail_query = f""" SELECT ANOMALYDETAILS FROM "{obj_name}" WHERE snap_time = TO_TIMESTAMP('{format_timestamp(anom_time)}', 'YYYY-MM-DD HH24:MI:SS.FF3') AND instance_number = {anom_inst} """ # 执行查询 cursor.execute(detail_query) details_result = cursor.fetchall() if not details_result: raise Exception(f"{obj_name}表中未找到匹配记录") # 获取XML数据 xml_data = details_result[0][0] # 处理XML命名空间问题 xml_data = re.sub(r'\sxmlns="[^"]+"', '', xml_data, count=1) root = ET.fromstring(xml_data) attributes = [] for attr in root.findall('.//Attribute'): try: attr_data = { 'name': attr.get('name'), 'value': float(attr.get('actualValue')), 'weight': float(attr.get('weight')), 'rank': int(attr.get('rank')) } attributes.append(attr_data) except (TypeError, ValueError) as e: print(f"解析属性时出错: {e}") # 按rank排序 attributes.sort(key=lambda x: x['rank']) # 5. 交叉验证 - 获取异常点前10个采样点 print("获取异常点前10个采样点数据...") anomaly_df, B_total_std, C_col_std = get_data_samples(cursor, obj_name, anom_inst, anom_time) if anomaly_df is None: raise Exception(f"未找到异常点前10个采样点数据: {obj_name} at {anom_time} (实例 {anom_inst})") # 6. 获取正常模式数据 print("获取正常模式数据...") # P1: 一天前相同时间点(跳过休息日) p1_time = anom_time - timedelta(days=1) if is_holiday(p1_time): p1_time = find_previous_workday(p1_time) day_ago_df, A1_total_std, A2_col_std = get_data_samples(cursor, obj_name, anom_inst, p1_time) # P2: 一周前相同时间点 p2_time = anom_time - timedelta(weeks=1) week_ago_df, B1_total_std, B2_col_std = get_data_samples(cursor, obj_name, anom_inst, p2_time) # P3: 一个月前相同日期(跳过休息日) p3_time = anom_time - timedelta(days=30) if is_holiday(p3_time): p3_time = find_previous_workday(p3_time) month_ago_df, C1_total_std, C2_col_std = get_data_samples(cursor, obj_name, anom_inst, p3_time) # 如果正常模式数据为空,使用默认值 if day_ago_df is None: print("警告: 未找到一天前相同时间点数据") day_ago_df = pd.DataFrame() A1_total_std, A2_col_std = 0, {} if week_ago_df is None: print("警告: 未找到一周前相同时间点数据") week_ago_df = pd.DataFrame() B1_total_std, B2_col_std = 0, {} if month_ago_df is None: print("警告: 未找到一个月前相同日期数据") month_ago_df = pd.DataFrame() C1_total_std, C2_col_std = 0, {} # 7. 生成验证结果 validation_results = "\n\n===== 交叉验证结果 =====\n" m4_changes = [] m4_indicator_set = set() # 用于存储m4级别的指标名称 # 第一轮验证:总标准差比较 validation_results += "\n第一轮验证:总标准差比较\n" validation_results += f"异常模式总标准差 (B): {B_total_std:.4f}\n" # 比较1:B vs A1 if A1_total_std is not None: change_rate = get_relative_change(B_total_std, A1_total_std) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一天前相同时间点 (A1: {A1_total_std:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(comp_result.strip()) #m4_indicator_set.add("总标准差") # 比较2:B vs B1 if B1_total_std is not None: change_rate = get_relative_change(B_total_std, B1_total_std) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一周前相同时间点 (B1: {B1_total_std:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(comp_result.strip()) #m4_indicator_set.add("总标准差") # 比较3:B vs C1 if C1_total_std is not None: change_rate = get_relative_change(B_total_std, C1_total_std) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一个月前相同日期 (C1: {C1_total_std:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(comp_result.strip()) #m4_indicator_set.add("总标准差") # 第二轮验证:各列标准差比较 validation_results += "\n第二轮验证:各列标准差比较\n" # 只分析排名前3的指标 top_attributes = attributes[:3] for attr in top_attributes: col_name = attr['name'] validation_results += f"\n指标: {col_name} (当前值: {attr['value']}, 权重: {attr['weight']:.2f}, 排名: {attr['rank']})\n" # 异常模式该列标准差 col_std_b = C_col_std.get(col_name, 0) validation_results += f"异常模式标准差 (C): {col_std_b:.4f}\n" # 比较1:C vs A2 if A2_col_std and col_name in A2_col_std: col_std_a2 = A2_col_std[col_name] change_rate = get_relative_change(col_std_b, col_std_a2) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一天前相同时间点 (A2: {col_std_a2:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(f"指标 {col_name}: {comp_result.strip()}") m4_indicator_set.add(col_name) # 比较2:C vs B2 if B2_col_std and col_name in B2_col_std: col_std_b2 = B2_col_std[col_name] change_rate = get_relative_change(col_std_b, col_std_b2) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一周前相同时间点 (B2: {col_std_b2:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(f"指标 {col_name}: {comp_result.strip()}") m4_indicator_set.add(col_name) # 比较3:C vs C2 if C2_col_std and col_name in C2_col_std: col_std_c2 = C2_col_std[col_name] change_rate = get_relative_change(col_std_b, col_std_c2) change_desc, change_class = classify_change(change_rate) rate_str = format_change_rate(change_rate) comp_result = f"与一个月前相同日期 (C2: {col_std_c2:.4f}) 比较: 变化率 {rate_str} - {change_desc} ({change_class})\n" validation_results += comp_result if change_class == "m4": m4_changes.append(f"指标 {col_name}: {comp_result.strip()}") m4_indicator_set.add(col_name) # 7. 数据可视化 # 创建特定目录保存图片 timestamp_str = anom_time.strftime("%Y%m%d_%H%M%S") save_dir = os.path.join(PIC_DIR, f"{obj_name}_{anom_inst}_{timestamp_str}") if not os.path.exists(save_dir): os.makedirs(save_dir) # 准备所有样本数据用于聚类 all_data = [] labels = [] # 添加正常模式样本(只添加非空数据) if day_ago_df is not None and not day_ago_df.empty: all_data.append(day_ago_df) labels.extend(['Day Ago'] * len(day_ago_df)) if week_ago_df is not None and not week_ago_df.empty: all_data.append(week_ago_df) labels.extend(['Week Ago'] * len(week_ago_df)) if month_ago_df is not None and not month_ago_df.empty: all_data.append(month_ago_df) labels.extend(['Month Ago'] * len(month_ago_df)) # 添加异常样本 if anomaly_df is not None and not anomaly_df.empty: all_data.append(anomaly_df) labels.extend(['Anomaly'] * len(anomaly_df)) # 检查是否有足够数据 if len(all_data) == 0: print("警告: 没有足够的数据进行可视化") else: # 合并所有数据 combined_df = pd.concat(all_data, ignore_index=True) # 排除非数值列 exclude_cols = ['SNAP_TIME', 'INSTANCE_NUMBER', 'PRED', 'PROB', 'ANOMALYDETAILS'] numeric_cols = [col for col in combined_df.columns if col not in exclude_cols and combined_df[col].dtype in [np.int64, np.float64]] if not numeric_cols: print("警告: 没有数值列可用于可视化") else: # 提取数值数据 data = combined_df[numeric_cols].values # 标准化数据 scaler = StandardScaler() scaled_data = scaler.fit_transform(data) # 使用K-Means聚类 kmeans = KMeans(n_clusters=2, random_state=42) clusters = kmeans.fit_predict(scaled_data) # 使用PCA降维可视化 from sklearn.decomposition import PCA pca = PCA(n_components=2) reduced_data = pca.fit_transform(scaled_data) # 创建聚类图 plt.figure(figsize=(12, 8)) # 为不同标签使用不同颜色 colors = {'Day Ago': 'blue', 'Week Ago': 'green', 'Month Ago': 'purple', 'Anomaly': 'red'} # 绘制所有点 for label in set(labels): idx = [i for i, l in enumerate(labels) if l == label] plt.scatter(reduced_data[idx, 0], reduced_data[idx, 1], c=colors[label], label=label, alpha=0.6) # 标记异常点(红色) anomaly_idx = [i for i, l in enumerate(labels) if l == 'Anomaly'] plt.scatter(reduced_data[anomaly_idx, 0], reduced_data[anomaly_idx, 1], c='red', marker='x', s=100, label='Anomaly Points') plt.title('K-Means Clustering of System Metrics') plt.xlabel('Principal Component 1') plt.ylabel('Principal Component 2') plt.legend() plt.grid(True) # 保存聚类图 kmeans_path = os.path.join(save_dir, 'kmeans_clustering.png') plt.savefig(kmeans_path) plt.close() pic_paths.append(kmeans_path) print(f"保存K-Means聚类图到: {kmeans_path}") # 创建时间序列图 plt.figure(figsize=(15, 10)) # 为每个指标创建子图 num_plots = min(5, len(numeric_cols)) # 最多显示5个指标 fig, axes = plt.subplots(num_plots, 1, figsize=(15, 5*num_plots)) if num_plots == 1: axes = [axes] # 确保单图时axes是列表 for i, col in enumerate(numeric_cols[:num_plots]): ax = axes[i] # 绘制正常模式(只绘制非空数据) if day_ago_df is not None and not day_ago_df.empty: day_ago_df['SNAP_TIME'] = pd.to_datetime(day_ago_df['SNAP_TIME']) ax.plot(day_ago_df['SNAP_TIME'], day_ago_df[col], 'b-', label='Day Ago', alpha=0.7) if week_ago_df is not None and not week_ago_df.empty: week_ago_df['SNAP_TIME'] = pd.to_datetime(week_ago_df['SNAP_TIME']) ax.plot(week_ago_df['SNAP_TIME'], week_ago_df[col], 'g-', label='Week Ago', alpha=0.7) if month_ago_df is not None and not month_ago_df.empty: month_ago_df['SNAP_TIME'] = pd.to_datetime(month_ago_df['SNAP_TIME']) ax.plot(month_ago_df['SNAP_TIME'], month_ago_df[col], 'm-', label='Month Ago', alpha=0.7) # 绘制异常点 if anomaly_df is not None and not anomaly_df.empty: anomaly_df['SNAP_TIME'] = pd.to_datetime(anomaly_df['SNAP_TIME']) # 只绘制异常点 ax.scatter(anomaly_df['SNAP_TIME'], anomaly_df[col], c='red', s=50, label='Anomaly', zorder=5) ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d %H:%M')) ax.xaxis.set_major_locator(mdates.AutoDateLocator()) ax.set_title(f'Time Series: {col}') ax.set_xlabel('Time') ax.set_ylabel('Value') ax.legend(loc='upper right') ax.grid(True, linestyle='--', alpha=0.7) plt.setp(ax.get_xticklabels(), rotation=45, ha='right') plt.tight_layout() # 保存时间序列图 timeseries_path = os.path.join(save_dir, 'timeseries_comparison.png') plt.savefig(timeseries_path, bbox_inches='tight') plt.close() pic_paths.append(timeseries_path) print(f"保存时间序列图到: {timeseries_path}") # 评估哪种可视化更直观 evaluation = """ === 可视化方法评估 === 1. K-Means聚类图: - 优点: 展示数据点在高维空间的分布,可以清晰看到异常点与正常模式的分离 - 缺点: 需要降维处理,可能丢失部分信息;不直观展示时间变化 2. 时间序列图: - 优点: 直观展示指标随时间的变化趋势,容易识别异常点 - 缺点: 当指标过多时难以在同一图中展示 结论: 时间序列图更直观展示异常点信息,特别是当需要分析指标随时间的变化趋势时。 建议将时间序列图作为主要可视化工具,聚类图作为辅助分析工具。 """ print(evaluation) # 保存评估结论 with open(os.path.join(save_dir, 'visualization_evaluation.txt'), 'w') as f: f.write(evaluation) # 8. 生成分析报告 analysis_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") summary = f"检测到{len(attributes)}个异常指标" if m4_changes: summary += ",检测到指标有重大变化,请及时关注!" m4_warning = "\n\n===== 重大变化警告 =====\n" for i, change in enumerate(m4_changes, 1): m4_warning += f"{i}. {change}\n" summary += m4_warning details = f"异常分析报告 ({analysis_time})\n" details += f"目标对象: {obj_name}\n" details += f"异常时间: {anom_time}\n" details += f"实例编号: {anom_inst}\n" details += "="*50 + "\n" # 添加指标详情 for attr in attributes: details += ( f"指标 {attr['rank']}: {attr['name']}\n" f" 当前值: {attr['value']}\n" f" 影响权重: {attr['weight']:.2f}\n" f" 问题描述: {generate_description(attr)}\n\n" ) # 添加交叉验证结果 details += validation_results # 添加数据样本信息 details += "\n\n===== 数据样本详情 =====\n" # 定义要排除的列 exclude_columns = ['ANOMALYDETAILS'] # 异常点前10个采样点 if anomaly_df is not None: # 排除指定列 anomaly_df_display = anomaly_df.drop(columns=exclude_columns, errors='ignore') details += f"异常点前10个采样点:\n{anomaly_df_display.to_string()}\n\n" else: details += "异常点前10个采样点: 无数据\n\n" # 一天前相同时间点采样点 if day_ago_df is not None: # 排除指定列 day_ago_df_display = day_ago_df.drop(columns=exclude_columns, errors='ignore') details += f"一天前相同时间点采样点:\n{day_ago_df_display.to_string()}\n\n" else: details += "一天前相同时间点采样点: 无数据\n\n" # 一周前相同时间点采样点 if week_ago_df is not None: # 排除指定列 week_ago_df_display = week_ago_df.drop(columns=exclude_columns, errors='ignore') details += f"一周前相同时间点采样点:\n{week_ago_df_display.to_string()}\n\n" else: details += "一周前相同时间点采样点: 无数据\n\n" # 一个月前相同日期采样点 if month_ago_df is not None: # 排除指定列 month_ago_df_display = month_ago_df.drop(columns=exclude_columns, errors='ignore') details += f"一个月前相同日期采样点:\n{month_ago_df_display.to_string()}\n" else: details += "一个月前相同日期采样点: 无数据\n" print(details) # 9. 更新分析结果 # 根据是否有重大变化设置alert_status alert_status = "sending" if m4_changes else "normal" # 准备图片路径字符串 pic_paths_str = ",".join(pic_paths) if pic_paths else "无图片" # 准备m4指标字符串 m4_indicators_str = ",".join(m4_indicator_set) if m4_indicator_set else "无" update_query = """ UPDATE RT_RESULT SET alert_analysis_status = :status, alert_analysis_summary = :summary, alert_analysis_detail = :detail, alert_status = :alert_status, alert_analysis_pic = :pic_paths, alert_analysis_dar = :m4_indicators WHERE object_name = :obj_name AND anomalies_time = TO_TIMESTAMP(:anom_time, 'YYYY-MM-DD HH24:MI:SS.FF3') AND anomalies_inst_number = :inst_number """ params = { 'status': 'done', 'summary': summary, 'detail': details, 'alert_status': alert_status, 'pic_paths': pic_paths_str, 'm4_indicators': m4_indicators_str, 'obj_name': obj_name, 'anom_time': format_timestamp(anom_time), 'inst_number': anom_inst } cursor.execute(update_query, params) cursor.connection.commit() print("分析结果保存成功") print(f"图片路径: {pic_paths_str}") print(f"m4指标: {m4_indicators_str}") print(details) except Exception as e: print(f"处理失败: {str(e)}") # 更新状态为error if obj_name and anom_time and anom_inst is not None: error_update = f""" UPDATE RT_RESULT SET alert_analysis_status = 'error' WHERE object_name = '{escape_sql(obj_name)}' AND anomalies_time = TO_TIMESTAMP('{format_timestamp(anom_time)}', 'YYYY-MM-DD HH24:MI:SS.FF3') AND anomalies_inst_number = {anom_inst} """ try: if cursor: cursor.execute(error_update) cursor.connection.commit() except Exception as inner_e: print(f"更新错误状态时出错: {str(inner_e)}") finally: # 关闭游标 if cursor: cursor.close() # 关闭连接 if conn: conn.close() print("数据库连接关闭") if __name__ == "__main__": analyze_anomalies() ====================== 以上代码在《创建时间序列图》这段代码中画出来的图上图所示 都是按天,我的采样点比较密集,就容易混到一起,是否能按照我采集的数据来做图, 没有日期显示的就不用画出图了,也就是绘制时间序列时跳过无数据的区间。 请修改以上的相关代码段

import sys import requests import json import re from pygments import highlight # 新增高亮库 from pygments.lexers import JsonLexer from pygments.formatters import Terminal256Formatter from conversion import JsonToExcelConverter import pandas as pd from datetime import datetime #飞书多维表格操作,对外提供接口读、写表格 class FeishuTable: def __init__(self): self.getRecordsId(app_token="SLfgb7Oc0aRNe9sGUCFcZsVhnDe") self.getRecordsId(table_id="tblduaK1bEJAT1ZV") #获取多维表格所有的record_id @classmethod def getRecordsId(cls, app_token, table_id, page_size=100, page_token=None): """ 从飞书多维表格中读取所有记录,并支持分页处理。 参数: - app_token (str): 多维表格应用的唯一标识符。 - table_id (str): 数据表的唯一标识符。 - page_size (int, optional): 每页返回的记录数量,默认值为100,最大值为500。 - page_token (str, optional): 分页标记,用于获取下一页数据。 返回: - dict: 包含所有记录的字典,结构为 {'data': {'items': all_records}}。 """ url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records" headers = cls.getHeaders() all_records = [] while True: if page_token: url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records?page_size={page_size}&page_token={page_token}" else: url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records?page_size={page_size}" response = requests.request("GET", url, headers=headers) response.raise_for_status() # 检查请求是否成功 parsed_response = response.json() all_records.extend(parsed_response['data']['items']) page_token = parsed_response['data'].get('page_token') if not page_token: break return {'data': {'items': all_records}} # 封装一个美观输出的样式并只过滤需要发布的信息 @classmethod def print_highlighted_json(cls, data, indent=4, ensure_ascii=False, sort_keys=True, style="solarized-dark"): """ 改进后的高亮JSON输出方法 """ formatted_json = json.dumps( data, indent=indent, ensure_ascii=ensure_ascii, # 保留中文显示 sort_keys=sort_keys ) # 生成带颜色代码的字符串 highlighted = highlight( formatted_json, JsonLexer(), # JSON词法分析器 Terminal256Formatter(style=style) # 终端兼容格式 ) print(highlighted) # 直接输出到控制台 @classmethod def filter_export_excel(cls, data, filter_key="发布", filter_value="是", output_prefix="filtered_data"): """ 新增数据过滤导出方法 参数: data: 原始JSON数据 filter_key: 过滤键名(默认"发布") filter_value: 过滤键值(默认"是") output_prefix: 输出文件名前缀 """ try: # 提取需要处理的数据层级(根据飞书数据结构调整) records = data.get('data', {}).get('records', []) # 过滤符合条件的记录 filtered = [ record for record in records if record.get(filter_key) == filter_value ] if not filtered: print("未找到匹配数据") return # 生成带时间戳的文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{output_prefix}_{timestamp}.xlsx" # 转换为DataFrame并保存 df = pd.DataFrame(filtered) df.to_excel(filename, index=False, engine='openpyxl') print(f"成功导出到文件:{filename}") except Exception as e: print(f"导出失败:{str(e)}") if __name__ == "__main__": table = FeishuTable records_ids = table.getRecordsId() # 生成格式化JSON字符串 formatted_json = table.print_highlighted_json(records_ids) # 导出过滤数据 FeishuTable.filter_export_excel(records_ids,filter_key="发布",filter_value="是") 报错了,帮我修一下

最新推荐

recommend-type

Comsol声子晶体能带计算:六角与三角晶格原胞选取及布里渊区高对称点选择 - 声子晶体 v1.0

内容概要:本文详细探讨了利用Comsol进行声子晶体能带计算过程中,六角晶格和三角晶格原胞选取的不同方法及其对简约布里渊区高对称点选择的影响。文中不仅介绍了两种晶格类型的基矢量定义方式,还强调了正确设置周期性边界条件(特别是相位补偿)的重要性,以避免计算误差如鬼带现象。同时,提供了具体的MATLAB代码片段用于演示关键步骤,并分享了一些实践经验,例如如何通过观察能带图中的狄拉克锥特征来验证路径设置的准确性。 适合人群:从事材料科学、物理学研究的专业人士,尤其是那些正在使用或计划使用Comsol软件进行声子晶体模拟的研究人员。 使用场景及目标:帮助研究人员更好地理解和掌握在Comsol环境中针对不同类型晶格进行精确的声子晶体能带计算的方法和技术要点,从而提高仿真精度并减少常见错误的发生。 其他说明:文章中提到的实际案例展示了因晶格类型混淆而导致的问题,提醒使用者注意细节差异,确保模型构建无误。此外,文中提供的代码片段可以直接应用于相关项目中作为参考模板。
recommend-type

springboot213大学生心理健康管理系统的设计与实现.zip

springboot213大学生心理健康管理系统的设计与实现
recommend-type

Web前端开发:CSS与HTML设计模式深入解析

《Pro CSS and HTML Design Patterns》是一本专注于Web前端设计模式的书籍,特别针对CSS(层叠样式表)和HTML(超文本标记语言)的高级应用进行了深入探讨。这本书籍属于Pro系列,旨在为专业Web开发人员提供实用的设计模式和实践指南,帮助他们构建高效、美观且可维护的网站和应用程序。 在介绍这本书的知识点之前,我们首先需要了解CSS和HTML的基础知识,以及它们在Web开发中的重要性。 HTML是用于创建网页和Web应用程序的标准标记语言。它允许开发者通过一系列的标签来定义网页的结构和内容,如段落、标题、链接、图片等。HTML5作为最新版本,不仅增强了网页的表现力,还引入了更多新的特性,例如视频和音频的内置支持、绘图API、离线存储等。 CSS是用于描述HTML文档的表现(即布局、颜色、字体等样式)的样式表语言。它能够让开发者将内容的表现从结构中分离出来,使得网页设计更加模块化和易于维护。随着Web技术的发展,CSS也经历了多个版本的更新,引入了如Flexbox、Grid布局、过渡、动画以及Sass和Less等预处理器技术。 现在让我们来详细探讨《Pro CSS and HTML Design Patterns》中可能包含的知识点: 1. CSS基础和选择器: 书中可能会涵盖CSS基本概念,如盒模型、边距、填充、边框、背景和定位等。同时还会介绍CSS选择器的高级用法,例如属性选择器、伪类选择器、伪元素选择器以及选择器的组合使用。 2. CSS布局技术: 布局是网页设计中的核心部分。本书可能会详细讲解各种CSS布局技术,包括传统的浮动(Floats)布局、定位(Positioning)布局,以及最新的布局模式如Flexbox和CSS Grid。此外,也会介绍响应式设计的媒体查询、视口(Viewport)单位等。 3. 高级CSS技巧: 这些技巧可能包括动画和过渡效果,以及如何优化性能和兼容性。例如,CSS3动画、关键帧动画、转换(Transforms)、滤镜(Filters)和混合模式(Blend Modes)。 4. HTML5特性: 书中可能会深入探讨HTML5的新标签和语义化元素,如`<article>`、`<section>`、`<nav>`等,以及如何使用它们来构建更加标准化和语义化的页面结构。还会涉及到Web表单的新特性,比如表单验证、新的输入类型等。 5. 可访问性(Accessibility): Web可访问性越来越受到重视。本书可能会介绍如何通过HTML和CSS来提升网站的无障碍访问性,比如使用ARIA标签(Accessible Rich Internet Applications)来增强屏幕阅读器的使用体验。 6. 前端性能优化: 性能优化是任何Web项目成功的关键。本书可能会涵盖如何通过优化CSS和HTML来提升网站的加载速度和运行效率。内容可能包括代码压缩、合并、避免重绘和回流、使用Web字体的最佳实践等。 7. JavaScript与CSS/HTML的交互: 在现代Web开发中,JavaScript与CSS及HTML的交云并用是不可或缺的。书中可能会讲解如何通过JavaScript动态地修改样式、操作DOM元素以及使用事件监听和响应用户交互。 8. Web框架和预处理器: 这本书可能会提到流行的Web开发框架和预处理器,比如Bootstrap、Foundation、Sass和Less等,它们是如何简化和加速开发流程的。 9. 测试和维护: 书中也可能包含关于如何测试网页以及如何持续优化和维护CSS和HTML代码的章节。例如,使用断言测试、自动化测试、性能分析工具等。 最后,鉴于文件名称列表中的“压缩包子文件”的表述,这可能是对“压缩包”文件的一种误译或误用,此处“压缩包”应该指的是包含该书籍PDF文件的压缩文件格式,如ZIP或RAR。而“Pro CSS and HTML Design Patterns.pdf”指的就是该书籍的PDF格式电子版文件。 以上所述,构成了《Pro CSS and HTML Design Patterns》一书可能包含的核心知识点。通过学习这些内容,Web前端开发者可以掌握更为高效和优雅的设计模式,从而在日常工作中更高效地解决实际问题。
recommend-type

Zotero 7数据同步:Attanger插件安装&设置,打造文献管理利器

/i.s3.glbimg.com/v1/AUTH_08fbf48bc0524877943fe86e43087e7a/internal_photos/bs/2018/L/j/ipKA8TRPyt4ptaG1DGww/t-m-01.png) # 1. Zotero 7数据同步简介 在数字时代,管理研究数据变得日益重要,而Zot
recommend-type

卷积神经网络的基础理论200字

<think>我们正在回答用户关于卷积神经网络(CNN)基础理论的询问。根据提供的引用资料,我们可以概述CNN的基本结构和工作原理。引用[1]提到CNN是一种前馈神经网络,信息从输入单向流动到输出。引用[2]指出CNN在图像处理等领域应用广泛,通过层次结构和参数调整实现模式识别。引用[3]说明CNN包含卷积层,使用卷积核提取局部特征,减少参数量,提高效率。引用[4]则提到了训练过程,包括前向传播、误差计算和反向传播(梯度下降)。因此,我们将从以下几个方面概述CNN:1.CNN的基本结构(卷积层、池化层、全连接层)2.卷积操作的工作原理(局部感受野、权重共享)3.训练过程(前向传播、损失函数、反
recommend-type

轻便实用的Java库类查询工具介绍

标题 "java2库类查询" 和描述表明,所提及的工具是一个专门用于查询Java库类的应用程序。此软件旨在帮助开发者快速地查找和引用Java的标准开发工具包(SDK)中包含的所有应用程序编程接口(API)类。通过这样的工具,开发者可以节省大量在官方文档或搜索引擎上寻找类定义和使用方法的时间。它被描述为轻巧且方便,这表明其占用的系统资源相对较少,同时提供直观的用户界面,使得查询过程简洁高效。 从描述中可以得出几个关键知识点: 1. Java SDK:Java的软件开发工具包(SDK)是Java平台的一部分,提供了一套用于开发Java应用软件的软件包和库。这些软件包通常被称为API,为开发者提供了编程界面,使他们能够使用Java语言编写各种类型的应用程序。 2. 库类查询:这个功能对于开发者来说非常关键,因为它提供了一个快速查找特定库类及其相关方法、属性和使用示例的途径。良好的库类查询工具可以帮助开发者提高工作效率,减少因查找文档而中断编程思路的时间。 3. 轻巧性:软件的轻巧性通常意味着它对计算机资源的要求较低。这样的特性对于资源受限的系统尤为重要,比如老旧的计算机、嵌入式设备或是当开发者希望最小化其开发环境占用空间时。 4. 方便性:软件的方便性通常关联于其用户界面设计,一个直观、易用的界面可以让用户快速上手,并减少在使用过程中遇到的障碍。 5. 包含所有API:一个优秀的Java库类查询软件应当能够覆盖Java所有标准API,这包括Java.lang、Java.util、Java.io等核心包,以及Java SE平台的所有其他标准扩展包。 从标签 "java 库 查询 类" 可知,这个软件紧密关联于Java编程语言的核心功能——库类的管理和查询。这些标签可以关联到以下知识点: - Java:一种广泛用于企业级应用、移动应用(如Android应用)、网站后端、大型系统和许多其他平台的编程语言。 - 库:在Java中,库是一组预打包的类和接口,它们可以被应用程序重复使用。Java提供了庞大的标准库,以支持各种常见的任务和功能。 - 查询:查询指的是利用软件工具搜索、定位和检索信息的过程。对于Java库类查询工具来说,这意味着可以通过类名、方法签名或其他标识符来查找特定的API条目。 最后,压缩包文件列表包含了两个文件:“java.dit”和“Java.exe”。其中“Java.exe”很可能是程序的可执行文件,而“java.dit”可能是一个数据文件,用于存储Java类的索引或数据。由于文件名后缀通常与文件类型相关联,但“dit”并不是一个常见的文件扩展名。这可能是一个特定于软件的自定义格式,或是一个打字错误。 总结来说,"java2库类查询" 是一个针对Java开发者的实用工具,它提供了一个轻量级、易用的平台来查询和定位Java标准库中的所有类和API。此工具对优化开发流程,减少查找Java类文档的时间大有裨益,尤其适合需要频繁查阅Java API的开发者使用。
recommend-type

【Zotero 7终极指南】:新手必备!Attanger插件全攻略与数据同步神技

# 1. Zotero 7与Attanger插件的介绍 在当今的学术研究和知识管理领域,高效的文献管理工具至关重要。Zotero 7作为一个流行的参考文献管理软件,因其强大的功能和用户友好的界面而受到专业人士的青睐。而Attanger插件则为Zotero 7带来了更多定制化和高级功能,极大地增强
recommend-type

MATLAB整段注释快捷键

<think>首先,用户的问题是:“MATLAB整段注释快捷键”,意思是“MATLAB整段注释快捷键”。我需要用中文回答。 根据规则: - 回答必须用中文。 - 回答完问题后,必须在末尾输出“§§相关问题§§”,然后提出3-5个相关问题。 - “§§相关问题§§”必须完整输出在第一行,禁止换行分散。 - 相关问题必须基于用户的问题和我的回答。 现在,回忆MATLAB的快捷键:在MATLAB中,整段注释通常使用快捷键Ctrl + R来注释选中的代码行,使用Ctrl + T来取消注释。用户问的是“整段注释”,所以我应该确认快捷键。 在MATLAB中: - 注释选中的行:Ctrl + R -
recommend-type

Eclipse Jad反编译插件:提升.class文件查看便捷性

反编译插件for Eclipse是一个专门设计用于在Eclipse集成开发环境中进行Java反编译的工具。通过此类插件,开发者可以在不直接访问源代码的情况下查看Java编译后的.class文件的源代码,这在开发、维护和学习使用Java技术的过程中具有重要的作用。 首先,我们需要了解Eclipse是一个跨平台的开源集成开发环境,主要用来开发Java应用程序,但也支持其他诸如C、C++、PHP等多种语言的开发。Eclipse通过安装不同的插件来扩展其功能。这些插件可以由社区开发或者官方提供,而jadclipse就是这样一个社区开发的插件,它利用jad.exe这个第三方命令行工具来实现反编译功能。 jad.exe是一个反编译Java字节码的命令行工具,它可以将Java编译后的.class文件还原成一个接近原始Java源代码的格式。这个工具非常受欢迎,原因在于其反编译速度快,并且能够生成相对清晰的Java代码。由于它是一个独立的命令行工具,直接使用命令行可以提供较强的灵活性,但是对于一些不熟悉命令行操作的用户来说,集成到Eclipse开发环境中将会极大提高开发效率。 使用jadclipse插件可以很方便地在Eclipse中打开任何.class文件,并且将反编译的结果显示在编辑器中。用户可以在查看反编译的源代码的同时,进行阅读、调试和学习。这样不仅可以帮助开发者快速理解第三方库的工作机制,还能在遇到.class文件丢失源代码时进行紧急修复工作。 对于Eclipse用户来说,安装jadclipse插件相当简单。一般步骤包括: 1. 下载并解压jadclipse插件的压缩包。 2. 在Eclipse中打开“Help”菜单,选择“Install New Software”。 3. 点击“Add”按钮,输入插件更新地址(通常是jadclipse的更新站点URL)。 4. 选择相应的插件(通常名为“JadClipse”),然后进行安装。 5. 安装完成后重启Eclipse,插件开始工作。 一旦插件安装好之后,用户只需在Eclipse中双击.class文件,或者右键点击文件并选择“Open With Jadclipse”,就能看到对应的Java源代码。如果出现反编译不准确或失败的情况,用户还可以直接在Eclipse中配置jad.exe的路径,或者调整jadclipse的高级设置来优化反编译效果。 需要指出的是,使用反编译工具虽然方便,但要注意反编译行为可能涉及到版权问题。在大多数国家和地区,反编译软件代码属于合法行为,但仅限于学习、研究、安全测试或兼容性开发等目的。如果用户意图通过反编译获取商业机密或进行非法复制,则可能违反相关法律法规。 总的来说,反编译插件for Eclipse是一个强大的工具,它极大地简化了Java反编译流程,提高了开发效率,使得开发者在没有源代码的情况下也能有效地维护和学习Java程序。但开发者在使用此类工具时应遵守法律与道德规范,避免不当使用。
recommend-type

【进阶Python绘图】:掌握matplotlib坐标轴刻度间隔的高级技巧,让你的图表脱颖而出

# 摘要 本文系统地探讨了matplotlib库中坐标轴刻度间隔的定制与优化技术。首先概述了matplotlib坐标轴刻度间隔的基本概念及其在图表中的重要性,接