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: >
No description has been provided for this image
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)
No description has been provided for this image
No description has been provided for this image
In [69]:
df_heat['GC含量'].hist(bins=30, edgecolor='black', figsize=(6,4))
plt.suptitle('GC含量分布图')
plt.show() 
No description has been provided for this image
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()
No description has been provided for this image
In [71]:
#因为相关系数不高,所以先绘制散点图
sns.pairplot(df_heat[['Y染色体浓度','检测孕周','孕妇BMI']],kind='kde')
Out[71]:
<seaborn.axisgrid.PairGrid at 0x32a0da8a0>
No description has been provided for this image
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')
No description has been provided for this image
各主成分的方差贡献比例: [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]
No description has been provided for this image
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()
No description has been provided for this image
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()
No description has been provided for this image
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()
No description has been provided for this image
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()
No description has been provided for this image
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()
No description has been provided for this image