在前两篇文章中,我们通过肉眼和 BLS 算法分析了单颗恒星的光变曲线。但在真实的 Kepler 任务中,望远镜发现了数千个候选信号——其中许多是假阳性(如食双星、仪器噪声等)。
本文的目标是:利用 Pandas DataFrame 组织大规模候选体数据,训练一个随机森林分类器,让机器学习模型自动甄别真实的系外行星。
1. DataFrame 热身练习
1.1 创建玩具数据集
用三个模拟的系外行星候选体数据来熟悉 DataFrame 的基本操作。每条记录包含周期、凌星深度、持续时间、信噪比和文字描述:
import pandas as pd
warmup = pd.DataFrame([ { "case": "A", "period_days": 3.2, "depth_percent": 0.8, "duration_hours": 2.1, "snr": 14.0, "description": "regular repeated U-shaped dips" }, { "case": "B", "period_days": 8.5, "depth_percent": 0.1, "duration_hours": 1.0, "snr": 2.2, "description": "weak messy signal, not clearly repeating" }, { "case": "C", "period_days": 1.7, "depth_percent": 8.5, "duration_hours": 6.0, "snr": 20.0, "description": "very deep V-shaped repeated dips" },])
warmup输出:
case period_days depth_percent duration_hours snr \0 A 3.2 0.8 2.1 14.01 B 8.5 0.1 1.0 2.22 C 1.7 8.5 6.0 20.0
description0 regular repeated U-shaped dips1 weak messy signal, not clearly repeating2 very deep V-shaped repeated dips1.2 添加新列
为每个案例添加人工标注的分类标签。在天文学中,常见的分类包括:
candidate_like:类似真实行星候选体(U 形凹陷、高 SNR、周期性)weak_or_noise:信号太弱或噪声(低 SNR、无清晰重复)possible_binary:可能是食双星(很深的 V 形凹陷)
toy_df = warmup.copy()
toy_df["human_label"] = [ "candidate_like", "weak_or_noise", "possible_binary"]
toy_df输出:
case period_days depth_percent duration_hours snr \0 A 3.2 0.8 2.1 14.01 B 8.5 0.1 1.0 2.22 C 1.7 8.5 6.0 20.0
description human_label0 regular repeated U-shaped dips candidate_like1 weak messy signal, not clearly repeating weak_or_noise2 very deep V-shaped repeated dips possible_binary1.3 DataFrame 基础属性
print("Shape: ", toy_df.shape)print('Rows:', toy_df.shape[0])print('Columns:', toy_df.shape[1])输出:
Shape: (3, 7)Rows: 3Columns: 7shape 返回 (行数, 列数) 元组。
1.4 列筛选
选取特定列的子集。双层方括号 df[['col1', 'col2']] 返回 DataFrame,单层方括号 df['col'] 返回 Series:
toy_df[['case', 'period_days', 'depth_percent', 'snr']]输出:
case period_days depth_percent snr0 A 3.2 0.8 14.01 B 8.5 0.1 2.22 C 1.7 8.5 20.01.5 特征矩阵与标签向量
在机器学习中,特征(Features)是输入模型的数值列,标签(Labels)是要预测的目标:
feature_columns = [ 'period_days', 'depth_percent', 'duration_hours', 'snr']
toy_features = toy_df[feature_columns]toy_features输出:
period_days depth_percent duration_hours snr0 3.2 0.8 2.1 14.01 8.5 0.1 1.0 2.22 1.7 8.5 6.0 20.0toy_labels = toy_df['human_label']toy_labels输出:
0 candidate_like1 weak_or_noise2 possible_binaryName: human_label, dtype: object这就是监督学习的基本数据结构:X(特征矩阵) 和 y(标签向量)。
1.6 布尔条件筛选
high_snr = toy_df[toy_df['snr'] > 5]high_snr输出:
case period_days depth_percent duration_hours snr \0 A 3.2 0.8 2.1 14.02 C 1.7 8.5 6.0 20.0
description human_label0 regular repeated U-shaped dips candidate_like2 very deep V-shaped repeated dips possible_binaryweak_signals = toy_df[toy_df['snr'] <= 5]weak_signals输出:
case period_days depth_percent duration_hours snr \1 B 8.5 0.1 1.0 2.2
description human_label1 weak messy signal, not clearly repeating weak_or_noise1.7 多条件组合筛选
用 &(与)和括号组合多个条件:
deep_signals = toy_df[toy_df['depth_percent'] > 2.0]deep_signalsinteresting = toy_df[ (toy_df["snr"] > 5) & (toy_df["depth_percent"] < 5)]
interesting输出:
case period_days depth_percent duration_hours snr \0 A 3.2 0.8 2.1 14.0
description human_label0 regular repeated U-shaped dips candidate_like这个查询找出了”高 SNR 但深度适中”的信号——最有可能是真实行星的候选体。
1.8 排序
toy_df.sort_values("snr", ascending=False)输出:
case period_days depth_percent duration_hours snr \2 C 1.7 8.5 6.0 20.00 A 3.2 0.8 2.1 14.01 B 8.5 0.1 1.0 2.2
description human_label2 very deep V-shaped repeated dips possible_binary0 regular repeated U-shaped dips candidate_like1 weak messy signal, not clearly repeating weak_or_noisetoy_df.sort_values("depth_percent", ascending=False)1.9 值计数与布尔列
toy_df["human_label"].value_counts()输出:
human_labelcandidate_like 1weak_or_noise 1possible_binary 1Name: count, dtype: int64创建布尔列作为派生特征:
toy_df["strong_signal"] = toy_df["snr"] >= 7
toy_df[["case", "snr", "strong_signal", "human_label"]]输出:
case snr strong_signal human_label0 A 14.0 True candidate_like1 B 2.2 False weak_or_noise2 C 20.0 True possible_binarytoy_df["very_deep"] = toy_df["depth_percent"] > 5
toy_df[["case", "depth_percent", "very_deep", "human_label"]]1.10 列运算与分组聚合
创建派生数值列——简单评分 = SNR − 深度:
toy_df["simple_score"] = toy_df["snr"] - toy_df["depth_percent"]
toy_df[["case", "snr", "depth_percent", "simple_score", "human_label"]]输出:
case snr depth_percent simple_score human_label0 A 14.0 0.8 13.2 candidate_like1 B 2.2 0.1 2.1 weak_or_noise2 C 20.0 8.5 11.5 possible_binary按类别分组计算均值:
toy_df.groupby("human_label")[["period_days", "depth_percent", "duration_hours", "snr"]].mean()输出:
period_days depth_percent duration_hours snrhuman_labelcandidate_like 3.2 0.8 2.1 14.0possible_binary 1.7 8.5 6.0 20.0weak_or_noise 8.5 0.1 1.0 2.2groupby("human_label").mean() 按分类标签分组后计算每个特征的平均值。可以看到:candidate_like 类具有适中的深度和较高的 SNR,而 possible_binary 类深度很大。
1.11 准备机器学习数据结构
X_toy = toy_df[feature_columns]y_toy = toy_df["human_label"]
print("X shape:", X_toy.shape)print("y shape:", y_toy.shape)
display(X_toy)display(y_toy)输出:
X shape: (3, 4)y shape: (3,)
period_days depth_percent duration_hours snr0 3.2 0.8 2.1 14.01 8.5 0.1 1.0 2.22 1.7 8.5 6.0 20.0
0 candidate_like1 weak_or_noise2 possible_binaryName: human_label, dtype: object2. 大规模真实数据
2.1 环境配置
安装 scikit-learn 并导入所需库:
!pip -q install scikit-learnimport warningswarnings.filterwarnings("ignore")
import numpy as npimport pandas as pdimport matplotlib.pyplot as plt
import requestsfrom io import StringIOfrom IPython.display import display
from sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import ( accuracy_score, precision_score, recall_score, classification_report, confusion_matrix, ConfusionMatrixDisplay,)
RANDOM_STATE = 42
print("Libraries imported successfully.")print("numpy:", np.__version__)print("pandas:", pd.__version__)输出:
Libraries imported successfully.numpy: 2.3.5pandas: 2.3.32.2 查询 NASA 系外行星数据库
从 NASA Exoplanet Archive 下载 5000 条已知分类的记录——包括已确认行星(CONFIRMED)、候选体(CANDIDATE)和假阳性(FALSE POSITIVE):
TAP_URL = "https://exoplanetarchive.ipac.caltech.edu/TAP/sync"
query = '''select top 5000 kepid, kepoi_name, kepler_name, koi_disposition, koi_period, koi_duration, koi_depth, koi_prad, koi_model_snr, koi_kepmag, koi_scorefrom cumulativewhere koi_disposition in ('CONFIRMED', 'CANDIDATE', 'FALSE POSITIVE') and koi_period is not null and koi_duration is not null and koi_depth is not null and koi_prad is not null and koi_model_snr is not null and koi_kepmag is not null'''如果 NASA 查询失败,使用合成回退数据(make_fallback_archive 函数生成具有合理统计分布的人造数据):
def make_fallback_archive(n_confirmed=120, n_fp=120, n_candidate=60, seed=42): # Create a small synthetic fallback table if the live NASA query fails. rng = np.random.default_rng(seed)
rows = []
def add_rows(n, disposition): for i in range(n): if disposition == "CONFIRMED": period = 10 ** rng.uniform(np.log10(0.8), np.log10(25)) duration = rng.uniform(1.0, 8.0) depth = 10 ** rng.uniform(np.log10(200), np.log10(12000)) prad = 10 ** rng.uniform(np.log10(0.8), np.log10(12)) snr = 10 ** rng.uniform(np.log10(7), np.log10(80)) kepmag = rng.uniform(10, 15) elif disposition == "FALSE POSITIVE": period = 10 ** rng.uniform(np.log10(0.3), np.log10(40)) duration = rng.uniform(0.5, 16.0) depth = 10 ** rng.uniform(np.log10(1000), np.log10(100000)) prad = 10 ** rng.uniform(np.log10(5), np.log10(80)) snr = 10 ** rng.uniform(np.log10(3), np.log10(120)) kepmag = rng.uniform(11, 16) else: period = 10 ** rng.uniform(np.log10(0.5), np.log10(35)) duration = rng.uniform(0.7, 12.0) depth = 10 ** rng.uniform(np.log10(200), np.log10(50000)) prad = 10 ** rng.uniform(np.log10(1), np.log10(35)) snr = 10 ** rng.uniform(np.log10(4), np.log10(90)) kepmag = rng.uniform(10, 16)
rows.append({ "kepid": int(10_000_000 + len(rows)), "kepoi_name": f"K{len(rows):05d}.01", "kepler_name": None if disposition != "CONFIRMED" else f"Kepler-{len(rows)} b", "koi_disposition": disposition, "koi_period": period, "koi_duration": duration, "koi_depth": depth, "koi_prad": prad, "koi_model_snr": snr, "koi_kepmag": kepmag, "koi_score": np.nan, })
add_rows(n_confirmed, "CONFIRMED") add_rows(n_fp, "FALSE POSITIVE") add_rows(n_candidate, "CANDIDATE")
return pd.DataFrame(rows)
archive_df = None
try: print("Querying NASA Exoplanet Archive...") response = requests.get( TAP_URL, params={"query": query, "format": "csv"}, timeout=60, ) response.raise_for_status() archive_df = pd.read_csv(StringIO(response.text)) print("Live NASA rows downloaded:", len(archive_df))
except Exception as exc: print("NASA query failed. Using synthetic fallback data for the lesson.") print("Error:", exc) archive_df = make_fallback_archive()
print("Final table shape:", archive_df.shape)archive_df.head()输出:
Querying NASA Exoplanet Archive...Live NASA rows downloaded: 5000Final table shape: (5000, 11)
kepid kepoi_name kepler_name koi_disposition koi_period \0 10797460 K00752.01 Kepler-227 b CONFIRMED 9.4880361 10797460 K00752.02 Kepler-227 c CONFIRMED 54.4183832 10811496 K00753.01 NaN CANDIDATE 19.8991403 10848459 K00754.01 NaN FALSE POSITIVE 1.7369524 10854555 K00755.01 Kepler-664 b CONFIRMED 2.525592
koi_duration koi_depth koi_prad koi_model_snr koi_kepmag koi_score0 2.95750 615.8 2.26 35.8 15.347 1.0001 4.50700 874.8 2.83 25.8 15.347 0.9692 1.78220 10829.0 14.60 76.3 15.436 0.0003 2.40641 8079.2 33.46 505.6 15.597 0.0004 1.65450 603.3 2.75 40.9 15.509 1.000print('Columns:', list(archive_df.columns))输出:
Columns: ['kepid', 'kepoi_name', 'kepler_name', 'koi_disposition', 'koi_period', 'koi_duration', 'koi_depth', 'koi_prad', 'koi_model_snr', 'koi_kepmag', 'koi_score']3. 特征工程
3.1 对数变换
天文学数据通常是对数正态分布(数量级跨越很大)。取对数后数据更接近正态分布,模型训练效果更好:
def safe_log10(x): # Log10 transform that avoids problems with zero or negative values. x = pd.to_numeric(x, errors="coerce") return np.log10(np.clip(x, 1e-12, None))
features_df = archive_df.copy()
features_df["log_period"] = safe_log10(features_df["koi_period"])features_df["log_duration"] = safe_log10(features_df["koi_duration"])features_df["log_depth"] = safe_log10(features_df["koi_depth"])features_df["log_prad"] = safe_log10(features_df["koi_prad"])features_df["log_snr"] = safe_log10(features_df["koi_model_snr"])features_df["kepmag"] = pd.to_numeric(features_df["koi_kepmag"], errors="coerce")
FEATURES = [ "log_period", "log_duration", "log_depth", "log_prad", "log_snr", "kepmag",]
features_df = features_df.dropna(subset=FEATURES + ["koi_disposition"]).copy()
print("Feature columns:", FEATURES)features_df[["kepoi_name", "koi_disposition"] + FEATURES].head()输出:
Feature columns: ['log_period', 'log_duration', 'log_depth', 'log_prad', 'log_snr', 'kepmag']
kepoi_name koi_disposition log_period log_duration log_depth log_prad \0 K00752.01 CONFIRMED 0.977176 0.470925 2.789440 0.3541081 K00752.02 CONFIRMED 1.735746 0.653888 2.941909 0.4517862 K00753.01 CANDIDATE 1.298834 0.250956 4.034588 1.1643533 K00754.01 FALSE POSITIVE 0.239788 0.381370 3.907368 1.5245264 K00755.01 CONFIRMED 0.402363 0.218667 2.780533 0.439333
log_snr kepmag0 1.553883 15.3471 1.411620 15.3472 1.882525 15.4363 2.703807 15.5974 1.611723 15.509np.clip(x, 1e-12, None) 将所有 ≤0 的值替换为一个极小的正数(10⁻¹²),避免 log10(0) 或 log10(负数) 的数学错误。safe_log10 封装了这一安全处理。
3.2 划分训练集
只使用已知标签的数据(CONFIRMED = 1,FALSE POSITIVE = 0)作为训练集,CANDIDATE 留作后续预测:
known_df = features_df[ features_df["koi_disposition"].isin(["CONFIRMED", "FALSE POSITIVE"])].copy()
candidate_df = features_df[ features_df["koi_disposition"] == "CANDIDATE"].copy()
known_df["label"] = (known_df["koi_disposition"] == "CONFIRMED").astype(int)
print("Known examples:", known_df.shape)print("Candidate rows for later ranking:", candidate_df.shape)print("\nLabel counts:")print(known_df["label"].value_counts().rename(index={0: "false positive", 1: "confirmed"}))输出:
Known examples: (4194, 18)Candidate rows for later ranking: (806, 17)
Label counts:labelconfirmed 2526false positive 1668Name: count, dtype: int64.astype(int) 将布尔值 True/False 转换为 1/0,这是机器学习中二分类标签的标准格式。
3.3 类别平衡
如果确认行星(2526 个)远多于假阳性(1668 个),模型会偏向多数类。通过随机欠采样使两类数量相等:
confirmed = known_df[known_df["label"] == 1]false_positive = known_df[known_df["label"] == 0]
n = min(len(confirmed), len(false_positive))
if n < 20: print("Warning: very few examples. The model will be only a toy demonstration.")
confirmed_bal = confirmed.sample(n=n, random_state=RANDOM_STATE)false_positive_bal = false_positive.sample(n=n, random_state=RANDOM_STATE)
model_df = pd.concat([confirmed_bal, false_positive_bal], ignore_index=True)model_df = model_df.sample(frac=1, random_state=RANDOM_STATE).reset_index(drop=True)
print("Balanced training table:", model_df.shape)print(model_df["label"].value_counts().rename(index={0: "false positive", 1: "confirmed"}))
model_df[["kepoi_name", "koi_disposition"] + FEATURES + ["label"]].head()输出:
Balanced training table: (3336, 18)labelconfirmed 1668false positive 1668Name: count, dtype: int64
kepoi_name koi_disposition log_period log_duration log_depth log_prad \0 K01441.01 CONFIRMED 0.929775 0.468052 2.470557 0.2787541 K03084.01 FALSE POSITIVE 0.187637 0.231215 2.029789 -0.0362122 K00082.04 CONFIRMED 0.849503 0.440437 1.817565 -0.2006593 K03279.01 CONFIRMED 1.488943 0.624695 2.807061 0.3838154 K00351.01 CONFIRMED 2.520611 1.158483 3.920233 1.037028
log_snr kepmag label0 1.315970 15.135 11 1.082785 14.991 02 1.350248 11.492 13 1.143015 15.765 14 2.550228 13.804 1pd.concat 拼接两个 DataFrame,sample(frac=1) 以 100% 比例打乱行顺序,reset_index(drop=True) 重置索引。
3.4 特征空间可视化
以 log_depth 和 log_snr 为坐标轴,观察两类样本的分布:
plt.figure(figsize=(8, 5))
for label_value, label_name in [(1, "confirmed"), (0, "false positive")]: subset = model_df[model_df["label"] == label_value] plt.scatter( subset["log_depth"], subset["log_snr"], s=20, alpha=0.65, label=label_name, )
plt.xlabel("log10(transit depth [ppm])")plt.ylabel("log10(model SNR)")plt.title("Feature space: depth vs SNR")plt.legend()plt.grid(True)plt.show()
已确认行星(蓝色)倾向于集中在特定的深度-SNR 区域,而假阳性(橙色)分布更分散——这为机器学习分类器提供了可学习的信息。
4. 训练随机森林分类器
4.1 训练/测试拆分
train_test_split 将数据随机分为训练集(75%)和测试集(25%),stratify=y 确保两个集合中类别比例一致:
X = model_df[FEATURES]y = model_df["label"]
X_train, X_test, y_train, y_test, train_idx, test_idx = train_test_split( X, y, model_df.index, test_size=0.25, random_state=RANDOM_STATE, stratify=y,)
print("Training examples:", len(X_train))print("Test examples:", len(X_test))print("\nTrain label counts:")print(y_train.value_counts().rename(index={0: "false positive", 1: "confirmed"}))print("\nTest label counts:")print(y_test.value_counts().rename(index={0: "false positive", 1: "confirmed"}))输出:
Training examples: 2502Test examples: 834
Train label counts:labelfalse positive 1251confirmed 1251Name: count, dtype: int64
Test label counts:labelfalse positive 417confirmed 417Name: count, dtype: int644.2 训练模型
随机森林(Random Forest)是多个决策树的集成,每棵树在数据的随机子集上训练。参数说明:
n_estimators=300:300 棵决策树max_depth=5:每棵树最多 5 层(防止过拟合)min_samples_leaf=3:叶节点最少 3 个样本class_weight="balanced":自动处理类别不平衡
model = RandomForestClassifier( n_estimators=300, max_depth=5, min_samples_leaf=3, random_state=RANDOM_STATE, class_weight="balanced",)
model.fit(X_train, y_train)
print("Random Forest trained.")输出:
Random Forest trained.4.3 模型评估
在测试集上评估模型性能:
- 准确率(Accuracy):预测正确的比例
- 精确率(Precision):预测为”确认行星”中真正是行星的比例
- 召回率(Recall):真实行星中被正确找出的比例
y_pred = model.predict(X_test)y_proba = model.predict_proba(X_test)[:, 1]
acc = accuracy_score(y_test, y_pred)prec = precision_score(y_test, y_pred, zero_division=0)rec = recall_score(y_test, y_pred, zero_division=0)
print("Test accuracy:", round(acc, 3))print("Test precision:", round(prec, 3))print("Test recall:", round(rec, 3))
print("\nClassification report:")print(classification_report( y_test, y_pred, target_names=["false positive", "confirmed"], zero_division=0,))输出:
Test accuracy: 0.841Test precision: 0.817Test recall: 0.878
Classification report: precision recall f1-score support
false positive 0.87 0.80 0.83 417 confirmed 0.82 0.88 0.85 417
accuracy 0.84 834 macro avg 0.84 0.84 0.84 834 weighted avg 0.84 0.84 0.84 834predict_proba 返回每个样本属于各类的概率,[:, 1] 取第二列(确认行星的概率)。模型整体准确率 84%,对确认行星的召回率 88%——模型能找出大多数真实行星。
4.4 混淆矩阵
混淆矩阵可视化预测结果 vs 真实标签:
cm = confusion_matrix(y_test, y_pred, labels=[1, 0])
fig, ax = plt.subplots(figsize=(5, 5))
disp = ConfusionMatrixDisplay( confusion_matrix=cm, display_labels=["confirmed", "false positive"],)
disp.plot(ax=ax, values_format="d", colorbar=False)plt.title("Confusion matrix — test set")plt.grid(False)plt.show()
- 左上角(True Positive,366):正确识别为行星
- 右下角(True Negative,335):正确识别为假阳性
- 右上角(False Negative,51):行星被误判为假阳性(漏报)
- 左下角(False Positive,82):假阳性被误判为行星(误报)
4.5 分析分类错误的案例
test_rows = model_df.loc[test_idx].copy()test_rows["true_label"] = y_test.valuestest_rows["pred_label"] = y_predtest_rows["confirmed_probability"] = y_proba
test_rows["correct"] = test_rows["true_label"] == test_rows["pred_label"]
mistakes = test_rows[~test_rows["correct"]].copy()
print("Number of test mistakes:", len(mistakes))
mistakes[[ "kepid", "kepoi_name", "kepler_name", "koi_disposition", "koi_period", "koi_depth", "koi_prad", "koi_model_snr", "confirmed_probability", "true_label", "pred_label",]].sort_values("confirmed_probability", ascending=False).head(20)输出(前20条假阳性被误判为行星):
Number of test mistakes: 133
kepid kepoi_name kepler_name koi_disposition koi_period koi_depth \2496 5443604 K02713.01 NaN FALSE POSITIVE 21.391012 645.82139 8345172 K02385.01 NaN FALSE POSITIVE 50.571581 1282.61707 3120276 K01111.01 NaN FALSE POSITIVE 10.265477 399.6...
koi_prad koi_model_snr confirmed_probability true_label pred_label2496 1.72 25.1 0.834551 0 12139 2.74 19.9 0.834471 0 1这些被模型高置信度误判的假阳性,通常具有”看起来像行星”的物理参数:适中的周期、合理的凌星深度和小行星半径。这正是机器学习面临的挑战——有些假阳性在特征空间中与真实行星高度重叠。
4.6 特征重要性
随机森林可以告诉我们哪些特征对分类贡献最大:
importance = pd.DataFrame({ "feature": FEATURES, "importance": model.feature_importances_,}).sort_values("importance", ascending=True)
plt.figure(figsize=(8, 4))plt.barh(importance["feature"], importance["importance"])plt.xlabel("Feature importance")plt.title("Which features did the forest use most?")plt.grid(axis="x", alpha=0.3)plt.show()
importance.sort_values("importance", ascending=False)
通常 log_depth(凌星深度)和 log_prad(行星半径)是最重要的特征——深度越大、半径越大的信号越可能是假阳性(如食双星)。
4.7 过拟合检查
train_pred = model.predict(X_train)test_pred = model.predict(X_test)
train_acc = accuracy_score(y_train, train_pred)test_acc = accuracy_score(y_test, test_pred)
print("Train accuracy:", round(train_acc, 3))print("Test accuracy:", round(test_acc, 3))
if train_acc > test_acc + 0.15: print("Possible overfitting: train score is much higher than test score.")else: print("Train/test gap is not extremely large for this demo.")输出:
Train accuracy: 0.867Test accuracy: 0.841Train/test gap is not extremely large for this demo.训练集(86.7%)与测试集(84.1%)差距仅 2.6 个百分点,模型未严重过拟合。
5. 候选体排序(Candidate Triage)
5.1 对未分类候选体进行预测
Kepler 数据中有 806 条 CANDIDATE 状态的记录——它们的真实身份尚未被 NASA 确认。用训练好的模型对它们打分:
if len(candidate_df) == 0: print("No candidate rows available. Skipping candidate triage.")else: candidate_X = candidate_df[FEATURES] candidate_df = candidate_df.copy() candidate_df["confirmed_like_probability"] = model.predict_proba(candidate_X)[:, 1]
ranked_candidates = candidate_df.sort_values("confirmed_like_probability", ascending=False)
print("Ranked candidate rows:", len(ranked_candidates))
display_cols = [ "kepid", "kepoi_name", "kepler_name", "koi_period", "koi_depth", "koi_prad", "koi_model_snr", "koi_kepmag", "confirmed_like_probability", ]
display(ranked_candidates[display_cols].head(15))输出(前 15 个高概率候选体):
Ranked candidate rows: 806
kepid kepoi_name kepler_name koi_period koi_depth koi_prad \3144 4951877 K00501.01 NaN 24.796280 574.4 2.701561 4242147 K01934.01 NaN 28.782710 864.5 2.113400 5131180 K00641.01 NaN 14.851855 1341.0 2.73...
koi_model_snr koi_kepmag confirmed_like_probability3144 43.6 14.612 0.8369561561 40.1 14.649 0.836525模型按概率从高到低排列了所有候选体。这些最有可能的候选体具有:适中的对数深度、小行星半径(1.5−3 地球半径)、高 SNR——与已确认行星的特征高度吻合,值得天文学家优先进行后续观测验证。
6. 本节小结
本节完成了以下内容:
- 掌握了 Pandas DataFrame 的核心操作:创建、列筛选、布尔过滤、排序、
groupby分组聚合、新列派生。 - 构建了特征矩阵(X)和标签向量(y),为监督学习做好了数据准备。
- 从 NASA Exoplanet Archive 获取了 5000 条真实系外行星候选体数据。
- 实施了特征工程:对数变换(
safe_log10)使天文学数量级数据更适合机器学习模型。 - 训练了随机森林分类器(
RandomForestClassifier),在测试集上达到 84% 准确率和 88% 召回率。 - 分析了混淆矩阵和特征重要性,理解了模型的优势和局限性。
- 对 806 个未确认候选体进行了概率排序,实现了自动化候选体筛选。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)
.webp)