Orange3数据预处理(索引选择器组件)

组件描述
数据行即使在某些或全部原始变量被来自原始变量的计算变量替换时,也保持其身份。
此小部件获取两个数据表(“数据”和“数据子集”),它们可以追溯到同一来源。基于行身份而非实际数据,它会从“数据”中选择所有出现在“数据子集”中的行。

输入
数据:参考数据集
子集数据:要匹配的子集

输出
匹配数据:参考数据集中的子集与子集数据中的索引匹配
不匹配的数据:参考数据集中的子集与子集数据中的索引不匹配
带批注的数据:参考数据集,带有定义匹配项的附加列

按数据索引选择(Select by Data Index) 的典型用法是在转换后检索原始数据
使用PCA组件转换数据,在散点图中投影转换后的数据,在这里我们只能看到PCA 转换后的数据,而看不到原始特征。PCA通过将相关要素组合到新要素中,将较高维度的数据投影到较低维度。
相互关联的功能在视觉上模糊了群集,无助于训练模型并增加了复杂性。最佳拟合线称为PC1(主要组件1)。PC2是垂直于PC1(垂直相交)的最佳拟合线。

示例流程

import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats import warnings import os from datetime import datetime import warnings warnings.filterwarnings(&#39;ignore&#39;) # 设置中文显示 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False class SpectralDataPreprocessor: """ 光谱数据预处理类 专门用于处理B题中碳化硅和硅晶圆片的红外干涉光谱数据 """ def __init__(self): self.processed_data = {} self.anomaly_reports = {} print("光谱数据预处理器已初始化") print("=" * 60) def load_data(self, filepath): """ 加载Excel数据文件 Parameters: filepath: str, 文件路径 Returns: pandas.DataFrame: 原始数据 """ try: # 尝试读取Excel文件 data = pd.read_excel(filepath, header=None) # 检查数据维度 if data.shape[1] < 2: raise ValueError(f"数据列数不足,期望至少2列,实际{data.shape[1]}列") # 设置列名:第1列为波数,第2列为反射率 data.columns = [&#39;wavenumber&#39;, &#39;reflectance&#39;] + [f&#39;col_{i}&#39; for i in range(2, data.shape[1])] print(f"✓ 成功加载文件: {filepath}") print(f" 数据维度: {data.shape[0]} 行 × {data.shape[1]} 列") print(f" 前5行预览:") print(data.head()) print() return data except Exception as e: print(f"✗ 加载文件失败: {e}") return None def detect_and_remove_header(self, data): """ 检测并移除可能的标题行 Parameters: data: pandas.DataFrame, 原始数据 Returns: pandas.DataFrame: 移除标题行后的数据 """ # 检查第一行是否为数值 first_row_numeric = True try: pd.to_numeric(data.iloc[0, 0]) pd.to_numeric(data.iloc[0, 1]) except: first_row_numeric = False if not first_row_numeric: print(" 检测到标题行,已移除") data = data.iloc[1:].copy() return data def preprocess_data(self, data, filename): """ 预处理光谱数据的主函数 Parameters: data: pandas.DataFrame, 原始数据 filename: str, 文件名 Returns: dict: 预处理结果 """ print(f"开始预处理文件: {filename}") print("-" * 40) # 初始化结果字典 result = { &#39;filename&#39;: filename, &#39;original_shape&#39;: data.shape, &#39;clean_data&#39;: None, &#39;anomalies&#39;: { &#39;header_rows&#39;: 0, &#39;missing_values&#39;: [], &#39;negative_wavenumber&#39;: [], &#39;invalid_reflectance&#39;: [], &#39;duplicates&#39;: [], &#39;outliers&#39;: [] }, &#39;statistics&#39;: {} } # 创建数据副本进行处理 work_data = data.copy() # 1. 检测并移除标题行 original_length = len(work_data) work_data = self.detect_and_remove_header(work_data) result[&#39;anomalies&#39;][&#39;header_rows&#39;] = original_length - len(work_data) # 2. 数据类型转换 print(" 步骤1: 数据类型转换") work_data[&#39;wavenumber&#39;] = pd.to_numeric(work_data[&#39;wavenumber&#39;], errors=&#39;coerce&#39;) work_data[&#39;reflectance&#39;] = pd.to_numeric(work_data[&#39;reflectance&#39;], errors=&#39;coerce&#39;) # 3. 检查并处理缺失值 print(" 步骤2: 检查缺失值") missing_mask = work_data[&#39;wavenumber&#39;].isna() | work_data[&#39;reflectance&#39;].isna() missing_count = missing_mask.sum() if missing_count > 0: missing_indices = work_data[missing_mask].index.tolist() result[&#39;anomalies&#39;][&#39;missing_values&#39;] = missing_indices print(f" 发现 {missing_count} 个缺失值行,已移除") work_data = work_data.dropna().copy() else: print(" 未发现缺失值") # 4. 检查负波数或零波数 print(" 步骤3: 检查波数有效性") negative_wavenumber_mask = work_data[&#39;wavenumber&#39;] <= 0 negative_count = negative_wavenumber_mask.sum() if negative_count > 0: negative_indices = work_data[negative_wavenumber_mask].index.tolist() result[&#39;anomalies&#39;][&#39;negative_wavenumber&#39;] = negative_indices print(f" 发现 {negative_count} 个非正波数,已移除") work_data = work_data[work_data[&#39;wavenumber&#39;] > 0].copy() else: print(" 所有波数均为正值") # 5. 检查反射率范围 print(" 步骤4: 检查反射率有效性") invalid_reflectance_mask = (work_data[&#39;reflectance&#39;] < 0) | (work_data[&#39;reflectance&#39;] > 100) invalid_count = invalid_reflectance_mask.sum() if invalid_count > 0: invalid_indices = work_data[invalid_reflectance_mask].index.tolist() result[&#39;anomalies&#39;][&#39;invalid_reflectance&#39;] = invalid_indices print(f" 发现 {invalid_count} 个无效反射率值,已移除") work_data = work_data[(work_data[&#39;reflectance&#39;] >= 0) & (work_data[&#39;reflectance&#39;] <= 100)].copy() else: print(" 所有反射率值均在有效范围内(0-100%)") # 6. 检查重复波数 print(" 步骤5: 检查重复波数") duplicate_mask = work_data[&#39;wavenumber&#39;].duplicated() duplicate_count = duplicate_mask.sum() if duplicate_count > 0: duplicate_indices = work_data[duplicate_mask].index.tolist() result[&#39;anomalies&#39;][&#39;duplicates&#39;] = duplicate_indices print(f" 发现 {duplicate_count} 个重复波数,保留第一个值") work_data = work_data.drop_duplicates(subset=[&#39;wavenumber&#39;]).copy() else: print(" 未发现重复波数") # 7. 按波数排序 print(" 步骤6: 按波数排序") work_data = work_data.sort_values(&#39;wavenumber&#39;).reset_index(drop=True) # 8. 异常值检测(使用IQR方法) print(" 步骤7: 异常值检测(IQR方法)") Q1 = work_data[&#39;reflectance&#39;].quantile(0.25) Q3 = work_data[&#39;reflectance&#39;].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR outlier_mask = (work_data[&#39;reflectance&#39;] < lower_bound) | (work_data[&#39;reflectance&#39;] > upper_bound) outlier_count = outlier_mask.sum() if outlier_count > 0: outlier_indices = work_data[outlier_mask].index.tolist() result[&#39;anomalies&#39;][&#39;outliers&#39;] = outlier_indices print(f" 发现 {outlier_count} 个统计异常值") print(f" 异常值范围: < {lower_bound:.2f} 或 > {upper_bound:.2f}") # 对于光谱数据,建议保留异常值,但标记出来 # work_data = work_data[~outlier_mask].copy() print(f" 建议: 光谱数据的异常值可能包含重要信息,暂时保留") else: print(" 未发现统计异常值") # 9. 计算最终统计信息 print(" 步骤8: 计算统计信息") result[&#39;statistics&#39;] = { &#39;final_count&#39;: len(work_data), &#39;wavenumber_range&#39;: [work_data[&#39;wavenumber&#39;].min(), work_data[&#39;wavenumber&#39;].max()], &#39;reflectance_range&#39;: [work_data[&#39;reflectance&#39;].min(), work_data[&#39;reflectance&#39;].max()], &#39;mean_reflectance&#39;: work_data[&#39;reflectance&#39;].mean(), &#39;std_reflectance&#39;: work_data[&#39;reflectance&#39;].std(), &#39;median_reflectance&#39;: work_data[&#39;reflectance&#39;].median(), &#39;total_removed_count&#39;: result[&#39;original_shape&#39;][0] - len(work_data), &#39;data_density&#39;: len(work_data) / (work_data[&#39;wavenumber&#39;].max() - work_data[&#39;wavenumber&#39;].min()) } result[&#39;clean_data&#39;] = work_data # 打印统计摘要 stats = result[&#39;statistics&#39;] print(f"\n 预处理完成:") print(f" 原始数据: {result[&#39;original_shape&#39;][0]} 行") print(f" 清洗后数据: {stats[&#39;final_count&#39;]} 行") print( f" 移除数据: {stats[&#39;total_removed_count&#39;]} 行 ({stats[&#39;total_removed_count&#39;] / result[&#39;original_shape&#39;][0] * 100:.1f}%)") print(f" 波数范围: {stats[&#39;wavenumber_range&#39;][0]:.1f} - {stats[&#39;wavenumber_range&#39;][1]:.1f} cm⁻¹") print(f" 反射率范围: {stats[&#39;reflectance_range&#39;][0]:.2f} - {stats[&#39;reflectance_range&#39;][1]:.2f} %") print(f" 平均反射率: {stats[&#39;mean_reflectance&#39;]:.2f} ± {stats[&#39;std_reflectance&#39;]:.2f} %") print() return result def save_cleaned_data(self, result, output_dir="cleaned_data"): """ 保存清洗后的数据 Parameters: result: dict, 预处理结果 output_dir: str, 输出目录 """ if result[&#39;clean_data&#39;] is None or len(result[&#39;clean_data&#39;]) == 0: print(f" ✗ {result[&#39;filename&#39;]}: 没有有效数据可保存") return # 创建输出目录 os.makedirs(output_dir, exist_ok=True) # 生成输出文件名 base_name = result[&#39;filename&#39;].replace(&#39;.xlsx&#39;, &#39;&#39;).replace(&#39;.xls&#39;, &#39;&#39;) output_path = os.path.join(output_dir, f"cleaned_{base_name}.xlsx") # 保存数据 clean_df = result[&#39;clean_data&#39;][[&#39;wavenumber&#39;, &#39;reflectance&#39;]].copy() clean_df.columns = [&#39;波数(cm-1)&#39;, &#39;反射率(%)&#39;] with pd.ExcelWriter(output_path, engine=&#39;openpyxl&#39;) as writer: # 保存清洗后的数据 clean_df.to_excel(writer, sheet_name=&#39;清洗后数据&#39;, index=False) # 保存统计信息 stats_df = pd.DataFrame([ [&#39;原始数据行数&#39;, result[&#39;original_shape&#39;][0]], [&#39;清洗后数据行数&#39;, result[&#39;statistics&#39;][&#39;final_count&#39;]], [&#39;移除行数&#39;, result[&#39;statistics&#39;][&#39;total_removed_count&#39;]], [&#39;移除比例(%)&#39;, f"{result[&#39;statistics&#39;][&#39;total_removed_count&#39;] / result[&#39;original_shape&#39;][0] * 100:.1f}"], [&#39;波数最小值(cm-1)&#39;, f"{result[&#39;statistics&#39;][&#39;wavenumber_range&#39;][0]:.2f}"], [&#39;波数最大值(cm-1)&#39;, f"{result[&#39;statistics&#39;][&#39;wavenumber_range&#39;][1]:.2f}"], [&#39;反射率最小值(%)&#39;, f"{result[&#39;statistics&#39;][&#39;reflectance_range&#39;][0]:.2f}"], [&#39;反射率最大值(%)&#39;, f"{result[&#39;statistics&#39;][&#39;reflectance_range&#39;][1]:.2f}"], [&#39;平均反射率(%)&#39;, f"{result[&#39;statistics&#39;][&#39;mean_reflectance&#39;]:.2f}"], [&#39;反射率标准差(%)&#39;, f"{result[&#39;statistics&#39;][&#39;std_reflectance&#39;]:.2f}"], [&#39;反射率中位数(%)&#39;, f"{result[&#39;statistics&#39;][&#39;median_reflectance&#39;]:.2f}"] ], columns=[&#39;统计项目&#39;, &#39;数值&#39;]) stats_df.to_excel(writer, sheet_name=&#39;统计信息&#39;, index=False) # 保存异常值报告 anomaly_data = [] for anomaly_type, indices in result[&#39;anomalies&#39;].items(): if isinstance(indices, list) and indices: anomaly_data.append( [anomaly_type, len(indices), str(indices[:10]) + (&#39;...&#39; if len(indices) > 10 else &#39;&#39;)]) elif isinstance(indices, (int, float)) and indices > 0: anomaly_data.append([anomaly_type, indices, &#39;&#39;]) if anomaly_data: anomaly_df = pd.DataFrame(anomaly_data, columns=[&#39;异常类型&#39;, &#39;数量&#39;, &#39;样例索引&#39;]) anomaly_df.to_excel(writer, sheet_name=&#39;异常值报告&#39;, index=False) print(f" ✓ 清洗后数据已保存到: {output_path}") def plot_data_comparison(self, results, output_dir="plots"): """ 绘制数据清洗前后的对比图 Parameters: results: list, 预处理结果列表 output_dir: str, 图片输出目录 """ os.makedirs(output_dir, exist_ok=True) # 设置中文字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False for result in results: if result[&#39;clean_data&#39;] is None: continue fig, axes = plt.subplots(2, 3, figsize=(18, 12)) fig.suptitle(f&#39;{result["filename"]} - 数据预处理对比分析&#39;, fontsize=16, fontweight=&#39;bold&#39;) clean_data = result[&#39;clean_data&#39;] # 1. 清洗后的光谱曲线 axes[0, 0].plot(clean_data[&#39;wavenumber&#39;], clean_data[&#39;reflectance&#39;], &#39;b-&#39;, linewidth=1.5, alpha=0.8) axes[0, 0].set_title(&#39;清洗后的光谱数据&#39;, fontsize=12, fontweight=&#39;bold&#39;) axes[0, 0].set_xlabel(&#39;波数 (cm$^{-1}$)&#39;) axes[0, 0].set_ylabel(&#39;反射率 (%)&#39;) axes[0, 0].grid(True, alpha=0.3) # 2. 反射率分布直方图 axes[0, 1].hist(clean_data[&#39;reflectance&#39;], bins=50, alpha=0.7, color=&#39;skyblue&#39;, edgecolor=&#39;black&#39;) axes[0, 1].axvline(clean_data[&#39;reflectance&#39;].mean(), color=&#39;red&#39;, linestyle=&#39;--&#39;, linewidth=2, label=f&#39;均值: {clean_data["reflectance"].mean():.2f}%&#39;) axes[0, 1].axvline(clean_data[&#39;reflectance&#39;].median(), color=&#39;green&#39;, linestyle=&#39;--&#39;, linewidth=2, label=f&#39;中位数: {clean_data["reflectance"].median():.2f}%&#39;) axes[0, 1].set_title(&#39;反射率分布&#39;, fontsize=12, fontweight=&#39;bold&#39;) axes[0, 1].set_xlabel(&#39;反射率 (%)&#39;) axes[0, 1].set_ylabel(&#39;频次&#39;) axes[0, 1].legend() axes[0, 1].grid(True, alpha=0.3) # 3. 数据质量统计 quality_data = [ result[&#39;statistics&#39;][&#39;final_count&#39;], result[&#39;statistics&#39;][&#39;total_removed_count&#39;] ] quality_labels = [&#39;保留数据&#39;, &#39;移除数据&#39;] colors = [&#39;lightgreen&#39;, &#39;lightcoral&#39;] wedges, texts, autotexts = axes[0, 2].pie(quality_data, labels=quality_labels, colors=colors, autopct=&#39;%1.1f%%&#39;, startangle=90) axes[0, 2].set_title(&#39;数据质量分布&#39;, fontsize=12, fontweight=&#39;bold&#39;) # 4. 波数密度分析 axes[1, 0].scatter(clean_data[&#39;wavenumber&#39;], clean_data[&#39;reflectance&#39;], alpha=0.6, s=1) axes[1, 0].set_title(&#39;波数-反射率散点图&#39;, fontsize=12, fontweight=&#39;bold&#39;) axes[1, 0].set_xlabel(&#39;波数 (cm$^{-1}$)&#39;) axes[1, 0].set_ylabel(&#39;反射率 (%)&#39;) axes[1, 0].grid(True, alpha=0.3) # 5. 异常值统计 anomaly_types = [] anomaly_counts = [] for anomaly_type, indices in result[&#39;anomalies&#39;].items(): if isinstance(indices, list) and indices: anomaly_types.append(anomaly_type.replace(&#39;_&#39;, &#39; &#39;).title()) anomaly_counts.append(len(indices)) elif isinstance(indices, (int, float)) and indices > 0: anomaly_types.append(anomaly_type.replace(&#39;_&#39;, &#39; &#39;).title()) anomaly_counts.append(indices) if anomaly_types: bars = axes[1, 1].bar(range(len(anomaly_types)), anomaly_counts, color=&#39;orange&#39;, alpha=0.7) axes[1, 1].set_title(&#39;异常值检测结果&#39;, fontsize=12, fontweight=&#39;bold&#39;) axes[1, 1].set_xlabel(&#39;异常类型&#39;) axes[1, 1].set_ylabel(&#39;数量&#39;) axes[1, 1].set_xticks(range(len(anomaly_types))) # axes[1, 1].set_xticklabels(anomaly_types, rotation=45, ha=&#39;right&#39;) # 在柱子上添加数值标签 for bar, count in zip(bars, anomaly_counts): axes[1, 1].text(bar.get_x() + bar.get_width() / 2, bar.get_height() + max(anomaly_counts) * 0.01, str(count), ha=&#39;center&#39;, va=&#39;bottom&#39;) else: axes[1, 1].text(0.5, 0.5, &#39;未发现异常值&#39;, ha=&#39;center&#39;, va=&#39;center&#39;, transform=axes[1, 1].transAxes, fontsize=14) axes[1, 1].set_title(&#39;异常值检测结果&#39;, fontsize=12, fontweight=&#39;bold&#39;) axes[1, 1].grid(True, alpha=0.3) # 删除第六张子图的坐标框(隐藏该子图) axes[1, 2].axis(&#39;off&#39;) # 隐藏坐标框和内容 plt.tight_layout() # 保存图片 output_path = os.path.join(output_dir, f"{result[&#39;filename&#39;]}_analysis.png") plt.savefig(output_path, dpi=300, bbox_inches=&#39;tight&#39;) print(f" ✓ 分析图表已保存到: {output_path}") plt.close() def generate_comprehensive_report(self, results, output_dir="reports"): """ 生成综合预处理报告 Parameters: results: list, 所有文件的预处理结果 output_dir: str, 报告输出目录 """ os.makedirs(output_dir, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") report_path = os.path.join(output_dir, f"preprocessing_report_{timestamp}.txt") with open(report_path, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f: f.write("=" * 80 + "\n") f.write("光谱数据预处理综合报告\n") f.write("=" * 80 + "\n") f.write(f"生成时间: {datetime.now().strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)}\n") f.write(f"处理文件数量: {len(results)}\n\n") # 总体统计 total_original = sum(r[&#39;original_shape&#39;][0] for r in results) total_final = sum(r[&#39;statistics&#39;][&#39;final_count&#39;] for r in results if r[&#39;statistics&#39;]) total_removed = total_original - total_final f.write("总体统计:\n") f.write("-" * 40 + "\n") f.write(f"总原始数据行数: {total_original}\n") f.write(f"总清洗后行数: {total_final}\n") f.write(f"总移除行数: {total_removed}\n") f.write(f"总体数据保留率: {total_final / total_original * 100:.1f}%\n\n") # 各文件详细信息 for i, result in enumerate(results, 1): f.write(f"{i}. 文件: {result[&#39;filename&#39;]}\n") f.write("-" * 50 + "\n") if result[&#39;statistics&#39;]: stats = result[&#39;statistics&#39;] f.write(f"原始数据行数: {result[&#39;original_shape&#39;][0]}\n") f.write(f"清洗后数据行数: {stats[&#39;final_count&#39;]}\n") f.write(f"移除行数: {stats[&#39;total_removed_count&#39;]}\n") f.write(f"数据保留率: {stats[&#39;final_count&#39;] / result[&#39;original_shape&#39;][0] * 100:.1f}%\n") f.write(f"波数范围: {stats[&#39;wavenumber_range&#39;][0]:.2f} - {stats[&#39;wavenumber_range&#39;][1]:.2f} cm⁻¹\n") f.write( f"反射率范围: {stats[&#39;reflectance_range&#39;][0]:.2f} - {stats[&#39;reflectance_range&#39;][1]:.2f} %\n") f.write(f"平均反射率: {stats[&#39;mean_reflectance&#39;]:.2f} ± {stats[&#39;std_reflectance&#39;]:.2f} %\n") f.write(f"数据密度: {stats[&#39;data_density&#39;]:.2f} 点/cm⁻¹\n") # 异常值详情 f.write(f"\n异常值检测结果:\n") for anomaly_type, data in result[&#39;anomalies&#39;].items(): if isinstance(data, list) and data: f.write(f" - {anomaly_type}: {len(data)} 个\n") elif isinstance(data, (int, float)) and data > 0: f.write(f" - {anomaly_type}: {data} 个\n") else: f.write("处理失败,无统计信息\n") f.write("\n") # 数据质量评估 f.write("数据质量评估:\n") f.write("-" * 40 + "\n") quality_issues = [] for result in results: if result[&#39;statistics&#39;]: retention_rate = result[&#39;statistics&#39;][&#39;final_count&#39;] / result[&#39;original_shape&#39;][0] if retention_rate < 0.9: quality_issues.append(f"{result[&#39;filename&#39;]}: 数据保留率较低 ({retention_rate * 100:.1f}%)") # 检查数据密度 density = result[&#39;statistics&#39;][&#39;data_density&#39;] if density < 1: quality_issues.append(f"{result[&#39;filename&#39;]}: 数据密度较低 ({density:.2f} 点/cm⁻¹)") if quality_issues: f.write("发现的质量问题:\n") for issue in quality_issues: f.write(f" {issue}\n") else: f.write("✓ 所有文件的数据质量良好\n") f.write("\n") def main(): """ 主函数:执行完整的数据预处理流程 """ print(" 光谱数据预处理程序启动") print("=" * 60) # 创建预处理器实例 preprocessor = SpectralDataPreprocessor() # 定义要处理的文件 files = [ (&#39;附件1.xlsx&#39;, &#39;碳化硅_10度入射&#39;), (&#39;附件2.xlsx&#39;, &#39;碳化硅_15度入射&#39;), (&#39;附件3.xlsx&#39;, &#39;硅_10度入射&#39;), (&#39;附件4.xlsx&#39;, &#39;硅_15度入射&#39;) ] results = [] # 处理每个文件 for filepath, description in files: print(f"\n 处理文件: {filepath} ({description})") print("=" * 60) # 检查文件是否存在 if not os.path.exists(filepath): print(f" ✗ 文件不存在: {filepath}") print(f" 请确保文件位于当前目录中") continue # 加载数据 data = preprocessor.load_data(filepath) if data is None: continue # 预处理数据 result = preprocessor.preprocess_data(data, description) results.append(result) # 保存清洗后的数据 preprocessor.save_cleaned_data(result) # 生成可视化图表 if results: print("\n 生成可视化图表...") preprocessor.plot_data_comparison(results) # 生成综合报告 print("\n 生成综合报告...") preprocessor.generate_comprehensive_report(results) print("\n 所有数据预处理完成!") print("输出文件:") print(" - cleaned_data/: 清洗后的Excel文件") print(" - plots/: 数据分析图表") print(" - reports/: 综合处理报告") else: print("\n 没有成功处理任何文件") if __name__ == "__main__": main()(对代码进行检查和优化,确保代码结果的正确性和严谨性,可视化图的高级性)
09-06
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值