In [59]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
In [60]:
df = pd.read_excel('附件.xlsx',sheet_name=0)
df.head()
Out[60]:
| 序号 | 孕妇代码 | 年龄 | 身高 | 体重 | 末次月经 | IVF妊娠 | 检测日期 | 检测抽血次数 | 检测孕周 | ... | Y染色体浓度 | X染色体浓度 | 13号染色体的GC含量 | 18号染色体的GC含量 | 21号染色体的GC含量 | 被过滤掉读段数的比例 | 染色体的非整倍体 | 怀孕次数 | 生产次数 | 胎儿是否健康 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | A001 | 31 | 160.0 | 72.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230429 | 1 | 11w+6 | ... | 0.025936 | 0.038061 | 0.377069 | 0.389803 | 0.399399 | 0.027484 | NaN | 1 | 0 | 是 |
| 1 | 2 | A001 | 31 | 160.0 | 73.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230531 | 2 | 15w+6 | ... | 0.034887 | 0.059572 | 0.371542 | 0.384771 | 0.391706 | 0.019617 | NaN | 1 | 0 | 是 |
| 2 | 3 | A001 | 31 | 160.0 | 73.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230625 | 3 | 20w+1 | ... | 0.066171 | 0.075995 | 0.377449 | 0.390582 | 0.399480 | 0.022312 | NaN | 1 | 0 | 是 |
| 3 | 4 | A001 | 31 | 160.0 | 74.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230716 | 4 | 22w+6 | ... | 0.061192 | 0.052305 | 0.375613 | 0.389251 | 0.397212 | 0.023280 | NaN | 1 | 0 | 是 |
| 4 | 5 | A002 | 32 | 149.0 | 74.0 | 2023-11-09 00:00:00 | 自然受孕 | 20240219 | 1 | 13w+6 | ... | 0.059230 | 0.059708 | 0.380260 | 0.393618 | 0.404868 | 0.024212 | NaN | 2 | 1 | 否 |
5 rows × 31 columns
In [61]:
#转换孕周数据为天数
df_heat = df
def convert_week_to_day(s):
if 'w+' in s:
parts = s.replace(' ','').split('w+')
weeks = int(parts[0])
days = int(parts[1])
return 7 * weeks + days
elif 'W+' in s:
parts = s.replace(' ','').split('W+')
weeks = int(parts[0])
days = int(parts[1])
return 7 * weeks + days
elif 'w' in s:
parts = s.replace(' ','').split('w')
weeks = int(parts[0])
return 7 * weeks
else:
parts = s.replace(' ','').split('W')
weeks = int(parts[0])
return 7 * weeks
df_heat['检测孕周'] = df_heat['检测孕周'].apply(convert_week_to_day)
print(df_heat['检测孕周'])
0 83
1 111
2 141
3 160
4 97
...
1077 124
1078 81
1079 88
1080 95
1081 102
Name: 检测孕周, Length: 1082, dtype: int64
In [62]:
# numeric_cols = df.select_dtypes(include=np.number).columns
# # --- 步骤 3: 设置中文字体(可选) ---
# # 这一步可以防止图表中的中文显示为方块
# plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
# plt.rcParams['axes.unicode_minus'] = False
# # --- 步骤 4: 循环绘制每列的箱线图 ---
# for col in numeric_cols:
# plt.figure(figsize=(8, 6))
# # 使用箱线图,并指定x轴数据为当前列
# sns.boxplot(x=df[col])
# # 设置图表标题和轴标签
# plt.title(f'变量 "{col}" 的箱线图', fontsize=16)
# plt.xlabel(col, fontsize=12)
# plt.tight_layout()
# plt.show()
In [63]:
#转换是否健康,1健康0不健康
def healthy_convertion(h):
if h == '是':
return 1
else:
return 0
df_heat['胎儿是否健康'] = df_heat['胎儿是否健康'].apply(healthy_convertion)
print(df_heat['胎儿是否健康'])
0 1
1 1
2 1
3 1
4 0
..
1077 1
1078 1
1079 1
1080 1
1081 1
Name: 胎儿是否健康, Length: 1082, dtype: int64
In [64]:
print(df_heat.isnull().sum())
序号 0 孕妇代码 0 年龄 0 身高 0 体重 0 末次月经 12 IVF妊娠 0 检测日期 0 检测抽血次数 0 检测孕周 0 孕妇BMI 0 原始读段数 0 在参考基因组上比对的比例 0 重复读段的比例 0 唯一比对的读段数 0 GC含量 0 13号染色体的Z值 0 18号染色体的Z值 0 21号染色体的Z值 0 X染色体的Z值 0 Y染色体的Z值 0 Y染色体浓度 0 X染色体浓度 0 13号染色体的GC含量 0 18号染色体的GC含量 0 21号染色体的GC含量 0 被过滤掉读段数的比例 0 染色体的非整倍体 956 怀孕次数 0 生产次数 0 胎儿是否健康 0 dtype: int64
末次月经和染色体的非整倍体是有缺失值。前者需要填补,后者代表正常。 怀孕次数和生产次数之间的关系有意思。
类型变量: 'IVF妊娠','染色体的非整倍体','怀孕次数','生产次数','胎儿是否健康'
In [65]:
import pandas as pd
columns_to_average = [
'原始读段数',
'在参考基因组上比对的比例',
'重复读段的比例',
'唯一比对的读段数 ',
'GC含量',
'13号染色体的Z值',
'18号染色体的Z值',
'21号染色体的Z值',
'X染色体的Z值',
'Y染色体的Z值',
'Y染色体浓度',
'X染色体浓度',
'13号染色体的GC含量',
'18号染色体的GC含量',
'21号染色体的GC含量',
'被过滤掉读段数的比例'
]
# 1. 明确分组键
group_cols = ['孕妇代码', '检测抽血次数']
# 2. 找到所有非分组键且非求平均的列
# 这里我们假设 df_heat 中除了 group_cols 和 columns_to_average 之外的所有列都需要保留
columns_to_keep = [col for col in df_heat.columns if col not in group_cols and col not in columns_to_average]
# 3. 构建聚合字典,为每列指定聚合函数
agg_dict = {}
# 对要平均的列,使用 'mean'
for col in columns_to_average:
if col in df_heat.columns:
agg_dict[col] = 'mean'
# 对要保留的列,使用 'first'
for col in columns_to_keep:
if col in df_heat.columns:
agg_dict[col] = 'first'
# 4. 执行分组和聚合操作,并将结果重新赋值给 df_heat
df_heat = df_heat.groupby(group_cols, as_index=False).agg(agg_dict)
print("聚合并保留其他列后的 DataFrame df_heat:")
print(df_heat)
聚合并保留其他列后的 DataFrame df_heat:
孕妇代码 检测抽血次数 原始读段数 在参考基因组上比对的比例 重复读段的比例 唯一比对的读段数 GC含量 \
0 A001 1 5040534.0 0.806726 0.027603 3845411.0 0.399262
1 A001 2 3198810.0 0.806393 0.028271 2457402.0 0.393299
2 A001 3 3848846.0 0.803858 0.032596 2926292.0 0.399890
3 A001 4 5960269.0 0.802535 0.034762 4509561.0 0.397977
4 A002 1 4154302.0 0.805008 0.028855 3169114.0 0.403060
... ... ... ... ... ... ... ...
1016 A266 4 3328779.0 0.761121 0.033588 4223742.0 0.402899
1017 A267 1 5909723.0 0.805119 0.031026 3122280.0 0.402567
1018 A267 2 5858993.0 0.792959 0.029356 4350296.0 0.400457
1019 A267 3 4050243.0 0.793737 0.031787 4268949.0 0.398217
1020 A267 4 5769497.0 0.773181 0.027097 3513035.0 0.402411
13号染色体的Z值 18号染色体的Z值 21号染色体的Z值 ... 体重 末次月经 IVF妊娠 \
0 0.782097 -2.321212 -1.026003 ... 72.00 2023-02-01 00:00:00 自然受孕
1 0.692856 1.168521 -2.595099 ... 73.00 2023-02-01 00:00:00 自然受孕
2 -0.888702 -1.018236 -1.308662 ... 73.00 2023-02-01 00:00:00 自然受孕
3 0.498031 0.770401 -1.476955 ... 74.00 2023-02-01 00:00:00 自然受孕
4 -2.268039 -1.004015 0.863198 ... 74.00 2023-11-09 00:00:00 自然受孕
... ... ... ... ... ... ... ...
1016 0.763475 1.589868 -0.248327 ... 83.35 2022-12-29 自然受孕
1017 0.631217 -2.370587 -0.044406 ... 73.76 2023-02-25 自然受孕
1018 -0.208712 0.017227 0.149586 ... 74.06 2023-02-25 自然受孕
1019 1.008678 0.191082 1.117913 ... 74.74 2023-02-25 自然受孕
1020 1.435968 0.818162 -1.419471 ... 75.85 2023-02-25 自然受孕
检测日期 检测孕周 孕妇BMI 染色体的非整倍体 怀孕次数 生产次数 胎儿是否健康
0 20230429 83 28.125000 None 1 0 1
1 20230531 111 28.515625 None 1 0 1
2 20230625 141 28.515625 None 1 0 1
3 20230716 160 28.906250 None 1 0 1
4 20240219 97 33.331832 None 2 1 0
... ... ... ... ... ... ... ...
1016 2023-05-02 00:00:00 124 32.969881 T18 1 0 1
1017 2023-05-17 00:00:00 81 30.703133 T21 1 0 1
1018 2023-05-24 00:00:00 88 30.825814 None 1 0 1
1019 2023-05-31 00:00:00 95 31.107551 None 1 0 1
1020 2023-06-07 00:00:00 102 31.572341 None 1 0 1
[1021 rows x 31 columns]
In [66]:
drop_list = ['序号','孕妇代码','末次月经','IVF妊娠','检测日期','染色体的非整倍体','怀孕次数','生产次数','胎儿是否健康']
df_heat.drop(drop_list, axis=1, inplace=True)
In [67]:
corr_matrix = df_heat.corr()
# 尝试设置字体为 Arial Unicode MS
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
# 解决保存图像时负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(24,18))
sns.heatmap(
corr_matrix,
annot=True,
fmt=".2f",
cmap='coolwarm'
)
Out[67]:
<Axes: >
In [68]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# =========================
# 2. 封装绘图函数
# =========================
def plot_histograms(df, bins=30, title_size=18, label_size=14, split=2):
"""
按列绘制 df 的直方图,并分成多张图显示
参数:
df : DataFrame
bins : int, 直方图箱数
title_size : int, 每个子图标题字体大小
label_size : int, 坐标轴字体大小
split : int, 分成几张图
"""
cols = df.columns
n = len(cols)
step = (n + split - 1) // split # 每张图大约的列数
for i in range(split):
sub_cols = cols[i*step:(i+1)*step]
if len(sub_cols) == 0:
continue
axes = df[sub_cols].hist(
bins=bins,
edgecolor='black',
figsize=(24,10),
layout=(3,5), # 每张图最多 25 个子图
xlabelsize=label_size,
ylabelsize=label_size
)
# 调整子图标题字体大小
for ax in axes.flatten():
if ax is not None:
ax.set_title(ax.get_title(), fontsize=title_size)
plt.suptitle(f'数据分布直方图', y=1.02, fontsize=title_size+4)
plt.tight_layout(rect=[0, 0.03, 1, 0.96])
plt.show()
# =========================
# 3. 调用函数绘制
# =========================
plot_histograms(df_heat, bins=30, title_size=18, label_size=14, split=2)
In [69]:
df_heat['GC含量'].hist(bins=30, edgecolor='black', figsize=(6,4))
plt.suptitle('GC含量分布图')
plt.show()
In [70]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
# 假设 df_heat 是你的 DataFrame
# df_heat = pd.read_csv("your_data.csv") # 加载你的数据
data = df_heat['检测孕周']
# 创建图形对象和多个子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 设置标题
fig.suptitle("数据分布变换对比", fontsize=16)
# 原始数据分布
sns.histplot(data, kde=True, ax=axes[0, 0])
axes[0, 0].set_title("原始数据分布")
# 对数变换后的数据分布
log_data = np.log1p(data) # log1p 相当于 log(x + 1),处理 0 或负数时更安全
sns.histplot(log_data, kde=True, ax=axes[0, 1])
axes[0, 1].set_title("对数变换后的数据分布")
# 平方根变换后的数据分布
sqrt_data = np.sqrt(data)
sns.histplot(sqrt_data, kde=True, ax=axes[1, 0])
axes[1, 0].set_title("平方根变换后的数据分布")
# Box-Cox变换后的数据分布
# Box-Cox 变换要求数据必须是正数,故先去除负值或0
boxcox_data, _ = stats.boxcox(data[data > 0])
sns.histplot(boxcox_data, kde=True, ax=axes[1, 1])
axes[1, 1].set_title("Box-Cox变换后的数据分布")
# 展示所有子图
plt.tight_layout()
plt.subplots_adjust(top=0.9) # 调整标题位置
plt.show()
In [71]:
#因为相关系数不高,所以先绘制散点图
sns.pairplot(df_heat[['Y染色体浓度','检测孕周','孕妇BMI']],kind='kde')
Out[71]:
<seaborn.axisgrid.PairGrid at 0x32a0da8a0>
In [72]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# 假设 df_heat 是包含特征的 DataFrame
# df_heat = pd.read_csv("your_data.csv") # 加载你的数据
# 选择需要做 PCA 的特征(假设所有列都是特征)
data = df_heat.dropna() # 去除缺失值
# 标准化数据:PCA 对数据的标准化非常敏感
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data)
# 执行 PCA,保留足够解释 95% 方差的主成分
pca = PCA(n_components=0.95)
pca_result = pca.fit_transform(scaled_data)
# 查看主成分名称和对应的特征权重
pca_components = pd.DataFrame(pca.components_, columns=data.columns)
print("每个主成分与原始特征的关系:")
print(pca_components)
# 创建包含主成分的 DataFrame
pca_df = pd.DataFrame(data=pca_result, columns=[f'主成分{i+1}' for i in range(pca.n_components_)])
# 查看降维后的数据
print("\nPCA降维后的前几行数据:")
print(pca_df.head())
# 绘制主成分分析的结果(选择前两个主成分进行绘制)
plt.figure(figsize=(8, 6))
sns.scatterplot(x='主成分1', y='主成分2', data=pca_df, palette='viridis')
plt.title('PCA降维结果(前两个主成分)')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.show()
# 解释各主成分的方差贡献
print("\n各主成分的方差贡献比例:", pca.explained_variance_ratio_)
print("累计方差贡献比例:", np.cumsum(pca.explained_variance_ratio_))
# 绘制累计方差贡献图
plt.figure(figsize=(8, 6))
plt.plot(range(1, len(pca.explained_variance_ratio_) + 1), np.cumsum(pca.explained_variance_ratio_), marker='o')
plt.title('主成分的累计方差贡献')
plt.xlabel('主成分数量')
plt.ylabel('累计方差贡献比例')
plt.show()
每个主成分与原始特征的关系:
检测抽血次数 原始读段数 在参考基因组上比对的比例 重复读段的比例 唯一比对的读段数 GC含量 \
0 -0.144385 0.044598 0.028330 0.000616 0.048497 0.392141
1 0.303391 -0.151739 -0.027330 0.012542 -0.168245 0.128853
2 0.400278 -0.203555 -0.128049 0.123319 -0.218318 0.039155
3 0.026596 -0.080203 -0.032735 -0.050625 -0.065082 0.228685
4 0.267659 0.604650 -0.052581 0.026875 0.574150 0.053929
5 0.161433 0.066767 0.745426 -0.320278 0.134011 -0.057523
6 -0.007279 0.047490 0.095141 0.530199 0.049905 -0.065251
7 -0.077455 0.106723 -0.045218 0.615498 0.195751 0.039552
8 0.185174 -0.050924 -0.086722 0.049596 0.022639 -0.066711
9 -0.103687 0.012390 0.127496 0.001638 0.055946 -0.008196
10 -0.146754 0.035376 0.245873 -0.059220 0.033957 0.090717
11 -0.270748 -0.003930 0.061234 0.077828 -0.060365 -0.040099
12 0.068932 -0.195509 0.554794 0.429620 -0.136454 0.030166
13 0.155550 -0.104111 -0.007201 0.007194 -0.034576 0.027584
14 -0.036195 0.135488 -0.062657 -0.093663 -0.172592 0.260443
15 0.117696 -0.212067 -0.050529 -0.062354 0.296346 -0.259929
16 0.009468 0.120083 0.084177 -0.044183 -0.046401 0.337204
17 -0.107535 -0.028547 -0.004088 -0.005312 0.020317 -0.546636
13号染色体的Z值 18号染色体的Z值 21号染色体的Z值 X染色体的Z值 ... X染色体浓度 13号染色体的GC含量 \
0 -0.215283 -0.196789 -0.035944 -0.372879 ... 0.073862 0.377116
1 0.061047 0.167504 -0.024123 -0.026037 ... 0.045080 0.146841
2 -0.028617 -0.014472 0.038010 -0.066221 ... 0.422860 0.024312
3 0.429074 0.426089 0.112199 0.262796 ... -0.312626 0.284095
4 0.082308 0.025150 -0.018469 0.001444 ... 0.043528 0.063157
5 0.025352 0.045721 0.099223 -0.114361 ... 0.079003 -0.070319
6 0.069352 0.200195 -0.469028 -0.096591 ... 0.044956 -0.070544
7 0.084018 -0.075609 0.353639 0.022019 ... 0.027902 0.067670
8 -0.226661 -0.149215 0.690677 -0.024894 ... -0.205649 -0.056759
9 0.198501 -0.004232 0.170856 0.099575 ... 0.184342 0.048439
10 0.181715 -0.163996 0.008240 0.072073 ... 0.199372 0.109369
11 0.261803 0.469683 0.339299 -0.241761 ... 0.375126 -0.030341
12 -0.140945 -0.063088 0.016639 0.014443 ... -0.235602 0.004673
13 0.691104 -0.518026 -0.034715 -0.086606 ... -0.178932 0.046253
14 0.125429 0.045553 0.056789 -0.387085 ... 0.113523 -0.321908
15 0.038971 0.316472 -0.021638 -0.314231 ... -0.407807 -0.192504
16 -0.155620 0.175842 0.014650 0.518943 ... -0.029159 0.049188
17 0.087645 -0.113415 0.014374 0.341756 ... 0.283286 -0.072637
18号染色体的GC含量 21号染色体的GC含量 被过滤掉读段数的比例 年龄 身高 体重 \
0 0.376763 0.392543 -0.033894 0.065647 0.000618 -0.046401
1 0.129987 0.112413 -0.018144 0.055537 0.318477 0.549804
2 0.030234 0.040233 -0.031872 -0.050161 -0.226021 -0.283060
3 0.265280 0.171930 -0.048552 -0.003629 -0.194029 -0.241288
4 0.016252 0.022679 0.362191 -0.028961 0.005949 -0.010434
5 0.031242 -0.045869 -0.435746 -0.081235 -0.081818 -0.044203
6 -0.060982 -0.024553 -0.234240 0.544909 -0.019555 -0.073147
7 -0.035777 0.064880 -0.444841 -0.368441 -0.068572 0.093447
8 -0.029881 -0.009917 0.005933 0.540890 -0.054857 -0.044555
9 0.038545 0.084618 -0.054406 0.187390 0.712530 0.040560
10 0.062604 0.022236 0.150320 0.418198 -0.348230 0.078917
11 -0.014237 -0.228565 0.293317 -0.015658 -0.055701 0.036199
12 -0.055500 0.125550 0.543995 -0.186382 0.048779 -0.036198
13 -0.199407 -0.055349 0.023796 0.019132 0.003325 0.013049
14 -0.453437 0.468642 0.010665 -0.030479 -0.011480 -0.036610
15 0.008536 0.307173 -0.019549 0.040104 -0.024762 0.040622
16 -0.492341 0.195277 -0.071924 0.092495 -0.025282 0.038799
17 0.142986 0.602493 0.062472 -0.017228 -0.079060 0.007909
检测孕周 孕妇BMI
0 -0.171768 -0.060353
1 0.340415 0.478653
2 0.333667 -0.201772
3 0.121988 -0.173706
4 0.278072 -0.017657
5 0.171136 0.000203
6 0.101340 -0.079495
7 -0.101648 0.169743
8 0.123249 -0.025464
9 -0.094806 -0.434810
10 -0.213332 0.341013
11 -0.117021 0.087416
12 0.047203 -0.076463
13 -0.038846 0.013595
14 0.089288 -0.037748
15 -0.221505 0.069954
16 -0.141707 0.066711
17 0.070511 0.062477
[18 rows x 22 columns]
PCA降维后的前几行数据:
主成分1 主成分2 主成分3 主成分4 主成分5 主成分6 主成分7 \
0 0.659012 -2.890492 -1.696616 -0.863768 0.012511 0.039864 0.063842
1 -3.547612 -1.953898 -0.180595 -1.114193 -2.795003 1.047390 2.180439
2 0.345368 -0.860709 1.530192 -0.986217 -0.910531 0.282591 1.498121
3 -1.958589 -0.978744 0.643755 -0.030352 2.281898 0.431775 2.485525
4 2.764379 -1.462323 0.063771 -0.240276 -1.315041 0.232490 -0.663855
主成分8 主成分9 主成分10 主成分11 主成分12 主成分13 主成分14 \
0 -1.896055 0.122650 -0.002066 -0.053537 -0.189699 0.499366 1.865859
1 -2.334770 -0.872979 -0.536702 -1.428010 0.282949 -0.446142 0.678434
2 -0.881815 0.371131 -0.279419 -0.929093 -0.839447 0.891553 0.414044
3 0.049660 0.143584 -0.103427 -0.872864 -0.856737 0.559972 0.020700
4 -0.552292 1.617549 -1.941475 1.185887 0.009777 0.492653 -0.818745
主成分15 主成分16 主成分17 主成分18
0 0.227668 -0.938988 -0.489900 0.512347
1 0.223382 -0.334935 -0.257165 -0.163224
2 -0.042029 -0.905921 -0.436714 -0.018257
3 -0.209000 -0.478733 -0.231727 -0.077148
4 0.081451 -0.445636 0.347048 0.638496
/var/folders/38/dg2xxy_x0wl96nv1ssd3cg4r0000gn/T/ipykernel_57775/2094003536.py:36: UserWarning: Ignoring `palette` because no `hue` variable has been assigned. sns.scatterplot(x='主成分1', y='主成分2', data=pca_df, palette='viridis')
各主成分的方差贡献比例: [0.15711523 0.10308631 0.09892149 0.07919029 0.0747783 0.05349927 0.04949109 0.04704152 0.04533892 0.04188524 0.03814654 0.03231045 0.03021578 0.02687168 0.02178896 0.02077344 0.02022502 0.01918536] 累计方差贡献比例: [0.15711523 0.26020154 0.35912303 0.43831332 0.51309162 0.56659089 0.61608198 0.66312349 0.70846241 0.75034766 0.7884942 0.82080465 0.85102043 0.87789211 0.89968106 0.9204545 0.94067952 0.95986488]
In [73]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# 设置中文字体,确保图表标签正常显示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 你要绘制的列
columns_to_plot = ['Y染色体浓度', '检测孕周', '孕妇BMI']
# 绘制填充式的核密度图矩阵
sns.pairplot(
df_heat[columns_to_plot],
kind='kde',
# 设置非对角线图的参数,实现颜色填充
plot_kws={
'fill': True,
'cmap': 'viridis', # 颜色映射,可换成 'Blues', 'YlGnBu' 等
'levels': 15, # 等高线数量,越多颜色渐变越平滑
'alpha': 0.7 # 透明度
},
# 设置对角线图的参数,让其也有填充颜色
diag_kws={
'fill': True,
'color': 'skyblue',
'alpha': 0.6
}
)
plt.suptitle('变量关系核密度图矩阵', y=1.02, fontsize=16)
plt.show()
In [74]:
import pandas as pd
from pygam import LinearGAM, s
# 2. 定义变量
# 直接从 df_heat 中选取列
# .values 将 DataFrame 列转换为 NumPy 数组,这是 pyGAM 所需的格式
X = df_heat[['检测孕周', '孕妇BMI']].values # 自变量
y = df_heat['Y染色体浓度'].values # 因变量
# 3. 构建并拟合 GAM 模型
# s(0) 对应于 'gestational_age',s(1) 对应于 'maternal_BMI'
gam = LinearGAM(s(0) + s(1))
gam.fit(X, y)
# 4. 打印模型摘要
print("--- 模型拟合摘要 ---")
print(gam.summary())
--- 模型拟合摘要 ---
LinearGAM
=============================================== ==========================================================
Distribution: NormalDist Effective DoF: 23.0717
Link Function: IdentityLink Log Likelihood: -513669.2975
Number of Samples: 1021 AIC: 1027386.7384
AICc: 1027387.9504
GCV: 0.001
Scale: 0.001
Pseudo R-Squared: 0.1571
==========================================================================================================
Feature Function Lambda Rank EDoF P > x Sig. Code
================================= ==================== ============ ============ ============ ============
s(0) [0.6] 20 13.1 3.11e-15 ***
s(1) [0.6] 20 10.0 9.99e-16 ***
intercept 1 0.0 1.11e-16 ***
==========================================================================================================
Significance codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
WARNING: Fitting splines and a linear function to a feature introduces a model identifiability problem
which can cause p-values to appear significant when they are not.
WARNING: p-values calculated in this manner behave correctly for un-penalized models or models with
known smoothing parameters, but when smoothing parameters have been estimated, the p-values
are typically lower than they should be, meaning that the tests reject the null too readily.
None
/var/folders/38/dg2xxy_x0wl96nv1ssd3cg4r0000gn/T/ipykernel_57775/2236853311.py:18: UserWarning: KNOWN BUG: p-values computed in this summary are likely much smaller than they should be. Please do not make inferences based on these values! Collaborate on a solution, and stay up to date at: github.com/dswah/pyGAM/issues/163 print(gam.summary())
In [75]:
import matplotlib.pyplot as plt
from pygam import LinearGAM, s
import pandas as pd
# 假设 df_heat 和 gam 模型已经准备好
# 这里我们用之前的示例数据
X = df_heat[['检测孕周', '孕妇BMI']].values # 自变量
y = df_heat['Y染色体浓度'].values # 因变量
gam = LinearGAM(s(0) + s(1)).fit(X, y)
# 绘制拟合曲线
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 绘制孕周的平滑曲线
XX = gam.generate_X_grid(term=0)
axes[0].plot(XX[:, 0], gam.predict(XX), label='Fitted Curve')
axes[0].scatter(X[:, 0], y, c='gray', alpha=0.5, label='Actual Data')
axes[0].set_title('孕周与Y染色体浓度的关系')
axes[0].set_xlabel('检测孕周')
axes[0].set_ylabel('Y染色体浓度')
axes[0].legend()
# 绘制BMI的平滑曲线
XX = gam.generate_X_grid(term=1)
axes[1].plot(XX[:, 1], gam.predict(XX), label='Fitted Curve')
axes[1].scatter(X[:, 1], y, c='gray', alpha=0.5, label='Actual Data')
axes[1].set_title('孕妇BMI与Y染色体浓度的关系')
axes[1].set_xlabel('孕妇BMI')
axes[1].set_ylabel('Y染色体浓度')
axes[1].legend()
plt.tight_layout()
plt.show()
In [76]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# --- 准备数据(如果数据不在当前环境中,请运行此部分) ---
# 假设您有一个包含孕妇数据的DataFrame,名为df_heat
# 如果您的数据文件名和列名不同,请相应修改。
# 从您之前的文件中得知,数据中包含了 '孕妇BMI' 和 '检测孕周'。
# 这里我创建一个示例 DataFrame 以确保代码可独立运行。
try:
df_heat
except NameError:
print("df_heat 不存在,正在创建示例孕妇数据...")
data = {
'孕妇BMI': np.random.uniform(26, 47, 100),
'检测孕周': np.random.randint(8, 40, 100)
}
df_heat = pd.DataFrame(data)
# 您的分组区间数据示例
# 这里我根据您的描述定义了一个示例解决方案,您可以替换为您的实际数据
your_solutions = [
(np.array([39.53081909, 43.76147835]), np.array([14, 15, 19]))
]
# --- 绘图与数据处理 ---
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10, 8))
# 遍历每一个“解”(即一组BMI切分点和对应的推荐孕周)
for i, (bmi_breakpoints, recommended_weeks) in enumerate(your_solutions):
# 根据用户提供的BMI区间[26,47]和切分点构建bins
# bins的数量将比breakpoints多2个(一个起点,一个终点)
bins = [26] + list(bmi_breakpoints) + [47]
# labels的数量必须比bins少1,以匹配区间数量
# 这里我们有3个区间,所以需要3个标签
labels = [f'BMI Group {j+1}' for j in range(len(bins) - 1)]
# 使用 pd.cut() 为孕妇数据分配 BMI 区间标签
df_heat['bmi_group'] = pd.cut(df_heat['孕妇BMI'], bins=bins, labels=labels, include_lowest=True)
# 绘制散点图,并根据 BMI 分组着色
sns.scatterplot(
x='孕妇BMI',
y='检测孕周',
hue='bmi_group',
data=df_heat,
palette='viridis', # 使用viridis色盘
s=100, # 点的大小
alpha=0.7, # 透明度
ax=plt.gca() # 在当前的Axes上绘制
)
# 在图上标记每个区间的推荐孕周(y轴)
for j in range(len(bmi_breakpoints) + 1):
if j == 0:
bmi_range_text = f'BMI < {bmi_breakpoints[0]:.2f}'
x_pos = (26 + bmi_breakpoints[0]) / 2
elif j == len(bmi_breakpoints):
bmi_range_text = f'BMI > {bmi_breakpoints[-1]:.2f}'
x_pos = (bmi_breakpoints[-1] + 47) / 2
else:
bmi_range_text = f'BMI {bmi_breakpoints[j-1]:.2f} - {bmi_breakpoints[j]:.2f}'
x_pos = (bmi_breakpoints[j-1] + bmi_breakpoints[j]) / 2
# 绘制文本标注,显示推荐孕周
plt.text(
x_pos,
recommended_weeks[j],
f'推荐孕周: {recommended_weeks[j]}',
ha='center',
va='center',
fontsize=10,
color='black',
bbox=dict(facecolor='white', alpha=0.6, edgecolor='gray', boxstyle='round,pad=0.5')
)
plt.title('孕妇BMI与检测孕周关系图', fontsize=16)
plt.xlabel('孕妇BMI', fontsize=12)
plt.ylabel('检测孕周', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.6)
plt.legend(title='BMI 分组')
plt.tight_layout()
plt.show()
In [77]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体(可选,防止中文乱码)
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# --- 准备数据(请替换为您自己的数据) ---
try:
df_heat
except NameError:
print("df_heat 不存在,正在创建示例孕妇数据...")
data = {
'孕妇BMI': np.random.uniform(26, 47, 100),
'检测孕周': np.random.randint(56, 280, 100) # 天数
}
df_heat = pd.DataFrame(data)
# 示例解(BMI分割点 + 推荐孕周)
your_solutions = [
(np.array([39.53081909, 43.76147835]), np.array([14, 15, 19]))
]
# --- 绘图 ---
fig, ax = plt.subplots(figsize=(12, 8))
for i, (bmi_breakpoints, recommended_weeks) in enumerate(your_solutions):
bins = [-np.inf] + list(bmi_breakpoints) + [np.inf]
num_intervals = len(bmi_breakpoints) + 1
labels = [f'BMI Group {j+1}' for j in range(num_intervals)]
df_heat_copy = df_heat.copy()
df_heat_copy['bmi_group'] = pd.cut(df_heat_copy['孕妇BMI'], bins=bins, labels=labels)
df_heat_copy['检测孕周'] = df_heat_copy['检测孕周'] / 7 # 转换为周
scatter_plot = sns.scatterplot(
x='孕妇BMI',
y='检测孕周',
hue='bmi_group',
data=df_heat_copy,
palette='viridis',
s=100, alpha=0.7, ax=ax
)
# 获取X轴范围
x_min, x_max = df_heat_copy['孕妇BMI'].min(), df_heat_copy['孕妇BMI'].max()
for j in range(num_intervals):
if j == 0:
x_start = x_min
x_end = bmi_breakpoints[0]
elif j == num_intervals - 1:
x_start = bmi_breakpoints[-1]
x_end = x_max
else:
x_start = bmi_breakpoints[j-1]
x_end = bmi_breakpoints[j]
line_color = 'red' # 推荐线统一用红色
ax.hlines(
y=recommended_weeks[j],
xmin=x_start, xmax=x_end,
color=line_color, linestyle='--', linewidth=2,
label=f'推荐孕周 (Group {j+1}): {recommended_weeks[j]}'
)
ax.text((x_start + x_end) / 2, recommended_weeks[j] + 0.5,
f'{recommended_weeks[j]}周',
ha='center', va='bottom', fontsize=9, color=line_color,
bbox=dict(facecolor='white', alpha=0.5, edgecolor='none'))
# 图例优化
handles, labels = ax.get_legend_handles_labels()
# 去掉重复项
unique = dict(zip(labels, handles))
ax.legend(unique.values(), unique.keys(), title='图例', loc='upper left', bbox_to_anchor=(1, 1))
plt.title('孕妇BMI、检测孕周与推荐孕周关系图', fontsize=16)
plt.xlabel('孕妇BMI', fontsize=12)
plt.ylabel('检测孕周(周)', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.6)
plt.tight_layout(rect=[0, 0, 0.85, 1])
plt.show()
In [78]:
df.head()
Out[78]:
| 序号 | 孕妇代码 | 年龄 | 身高 | 体重 | 末次月经 | IVF妊娠 | 检测日期 | 检测抽血次数 | 检测孕周 | ... | Y染色体浓度 | X染色体浓度 | 13号染色体的GC含量 | 18号染色体的GC含量 | 21号染色体的GC含量 | 被过滤掉读段数的比例 | 染色体的非整倍体 | 怀孕次数 | 生产次数 | 胎儿是否健康 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | A001 | 31 | 160.0 | 72.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230429 | 1 | 83 | ... | 0.025936 | 0.038061 | 0.377069 | 0.389803 | 0.399399 | 0.027484 | NaN | 1 | 0 | 1 |
| 1 | 2 | A001 | 31 | 160.0 | 73.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230531 | 2 | 111 | ... | 0.034887 | 0.059572 | 0.371542 | 0.384771 | 0.391706 | 0.019617 | NaN | 1 | 0 | 1 |
| 2 | 3 | A001 | 31 | 160.0 | 73.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230625 | 3 | 141 | ... | 0.066171 | 0.075995 | 0.377449 | 0.390582 | 0.399480 | 0.022312 | NaN | 1 | 0 | 1 |
| 3 | 4 | A001 | 31 | 160.0 | 74.0 | 2023-02-01 00:00:00 | 自然受孕 | 20230716 | 4 | 160 | ... | 0.061192 | 0.052305 | 0.375613 | 0.389251 | 0.397212 | 0.023280 | NaN | 1 | 0 | 1 |
| 4 | 5 | A002 | 32 | 149.0 | 74.0 | 2023-11-09 00:00:00 | 自然受孕 | 20240219 | 1 | 97 | ... | 0.059230 | 0.059708 | 0.380260 | 0.393618 | 0.404868 | 0.024212 | NaN | 2 | 1 | 0 |
5 rows × 31 columns
In [58]:
#转换孕周数据为天数
df_draw = df
def convert_week_to_day(s):
if 'w+' in s:
parts = s.replace(' ','').split('w+')
weeks = int(parts[0])
days = int(parts[1])
return 7 * weeks + days
elif 'W+' in s:
parts = s.replace(' ','').split('W+')
weeks = int(parts[0])
days = int(parts[1])
return 7 * weeks + days
elif 'w' in s:
parts = s.replace(' ','').split('w')
weeks = int(parts[0])
return 7 * weeks
else:
parts = s.replace(' ','').split('W')
weeks = int(parts[0])
return 7 * weeks
df_draw['检测孕周'] = df_draw['检测孕周'].apply(convert_week_to_day)
print(df_draw['检测孕周'])
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[58], line 25 22 weeks = int(parts[0]) 23 return 7 * weeks ---> 25 df_draw['检测孕周'] = df_draw['检测孕周'].apply(convert_week_to_day) 26 print(df_draw['检测孕周']) File /opt/anaconda3/envs/mne/lib/python3.12/site-packages/pandas/core/series.py:4935, in Series.apply(self, func, convert_dtype, args, by_row, **kwargs) 4800 def apply( 4801 self, 4802 func: AggFuncType, (...) 4807 **kwargs, 4808 ) -> DataFrame | Series: 4809 """ 4810 Invoke function on values of Series. 4811 (...) 4926 dtype: float64 4927 """ 4928 return SeriesApply( 4929 self, 4930 func, 4931 convert_dtype=convert_dtype, 4932 by_row=by_row, 4933 args=args, 4934 kwargs=kwargs, -> 4935 ).apply() File /opt/anaconda3/envs/mne/lib/python3.12/site-packages/pandas/core/apply.py:1422, in SeriesApply.apply(self) 1419 return self.apply_compat() 1421 # self.func is Callable -> 1422 return self.apply_standard() File /opt/anaconda3/envs/mne/lib/python3.12/site-packages/pandas/core/apply.py:1502, in SeriesApply.apply_standard(self) 1496 # row-wise access 1497 # apply doesn't have a `na_action` keyword and for backward compat reasons 1498 # we need to give `na_action="ignore"` for categorical data. 1499 # TODO: remove the `na_action="ignore"` when that default has been changed in 1500 # Categorical (GH51645). 1501 action = "ignore" if isinstance(obj.dtype, CategoricalDtype) else None -> 1502 mapped = obj._map_values( 1503 mapper=curried, na_action=action, convert=self.convert_dtype 1504 ) 1506 if len(mapped) and isinstance(mapped[0], ABCSeries): 1507 # GH#43986 Need to do list(mapped) in order to get treated as nested 1508 # See also GH#25959 regarding EA support 1509 return obj._constructor_expanddim(list(mapped), index=obj.index) File /opt/anaconda3/envs/mne/lib/python3.12/site-packages/pandas/core/base.py:925, in IndexOpsMixin._map_values(self, mapper, na_action, convert) 922 if isinstance(arr, ExtensionArray): 923 return arr.map(mapper, na_action=na_action) --> 925 return algorithms.map_array(arr, mapper, na_action=na_action, convert=convert) File /opt/anaconda3/envs/mne/lib/python3.12/site-packages/pandas/core/algorithms.py:1743, in map_array(arr, mapper, na_action, convert) 1741 values = arr.astype(object, copy=False) 1742 if na_action is None: -> 1743 return lib.map_infer(values, mapper, convert=convert) 1744 else: 1745 return lib.map_infer_mask( 1746 values, mapper, mask=isna(values).view(np.uint8), convert=convert 1747 ) File pandas/_libs/lib.pyx:2999, in pandas._libs.lib.map_infer() Cell In[58], line 6, in convert_week_to_day(s) 5 def convert_week_to_day(s): ----> 6 if 'w+' in s: 7 parts = s.replace(' ','').split('w+') 8 weeks = int(parts[0]) TypeError: argument of type 'int' is not iterable
In [ ]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体(可选)
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# --- 数据预处理:每个孕妇只保留第一次 Y浓度 > 0.04 的记录 ---
df_selected = (
df_draw[(df_draw['Y染色体浓度'] >= 0.04) & (df_draw['孕妇BMI'] >= 26)] # 只要浓度 >= 0.04 的记录
.sort_values(['孕妇代码', '检测孕周']) # 先按孕妇ID和检测孕周排序
.groupby('孕妇代码', as_index=False) # 分组
.first() # 取每组的第一条
)
# --- 示例分组解 ---
your_solutions = [
(np.array([35.4, 40.8, 43.8]), np.array([14, 14, 20, 18]))
]
# --- 绘图 ---
fig, ax = plt.subplots(figsize=(12, 8))
for i, (bmi_breakpoints, recommended_weeks) in enumerate(your_solutions):
bins = [-np.inf] + list(bmi_breakpoints) + [np.inf]
num_intervals = len(bmi_breakpoints) + 1
labels = [f'BMI Group {j+1}' for j in range(num_intervals)]
df_copy = df_selected.copy()
df_copy['bmi_group'] = pd.cut(df_copy['孕妇BMI'], bins=bins, labels=labels)
df_copy['检测孕周'] = df_copy['检测孕周'] / 7 # 转换为周
scatter_plot = sns.scatterplot(
x='孕妇BMI',
y='检测孕周',
hue='bmi_group',
data=df_copy,
palette='viridis',
s=100, alpha=0.7, ax=ax
)
# 获取X轴范围
x_min, x_max = df_copy['孕妇BMI'].min(), df_copy['孕妇BMI'].max()
for j in range(num_intervals):
if j == 0:
x_start = x_min
x_end = bmi_breakpoints[0]
elif j == num_intervals - 1:
x_start = bmi_breakpoints[-1]
x_end = x_max
else:
x_start = bmi_breakpoints[j-1]
x_end = bmi_breakpoints[j]
line_color = 'red'
ax.hlines(
y=recommended_weeks[j],
xmin=x_start, xmax=x_end,
color=line_color, linestyle='--', linewidth=2,
label=f'推荐孕周 (Group {j+1}): {recommended_weeks[j]}'
)
ax.text((x_start + x_end) / 2, recommended_weeks[j] + 0.5,
f'{recommended_weeks[j]}周',
ha='center', va='bottom', fontsize=9, color=line_color,
bbox=dict(facecolor='white', alpha=0.5, edgecolor='none'))
# 图例优化
handles, labels = ax.get_legend_handles_labels()
unique = dict(zip(labels, handles))
ax.legend(unique.values(), unique.keys(), title='图例', loc='upper left', bbox_to_anchor=(1, 1))
plt.title('加噪版本孕妇BMI、检测孕周与推荐孕周关系图(首次Y浓度>0.04)', fontsize=16)
plt.xlabel('孕妇BMI', fontsize=12)
plt.ylabel('检测孕周(周)', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.6)
plt.tight_layout(rect=[0, 0, 0.85, 1])
plt.show()