如何计算不同大小矩阵的logfold change?代码问题排查
问题
我有一个包含基因及其互作关系的生物网络矩阵,基于它生成了排除特定基因及对应互作的另一个矩阵,两个Excel文件大小不同(第二个删除了部分行和列)。我需要按列成对计算logfold change,公式为log₂(excluded-value/normal-value)。处理规则如下:
- 为避免除零错误,若出现除零情况需将结果设为1(使
log₂(1)=0); - 当分子或分母为0时,用对应文件中的最小非零值乘以0.99替换,避免出现inf值。
但使用示例数据运行以下代码后结果不正确,怀疑是矩阵匹配或其他问题,烦请帮忙排查:
import pandas as pd import numpy as np np.seterr(divide='ignore') # input and output file locations in_loc = r"C:\Users\..\Downloads" out_loc = r"C:\Users\..\Downloads" # read input files as dataframes df_normal = pd.read_excel(in_loc + r"\example-normal.xlsx") df_excluded = pd.read_excel(in_loc + r"\example-excluded.xlsx") # set Node as index for correct calculation df_normal = df_normal.set_index("Node") df_excluded = df_excluded.set_index("Node") # find common indices between the two dataframes common_indices = df_normal.index.intersection(df_excluded.index) # subset dataframes with common indices df_normal_matched = df_normal.loc[common_indices] df_excluded_matched = df_excluded.loc[common_indices] # replace denominator values that are 0 with the next smallest non-zero value min_normal = df_normal_matched[df_normal_matched > 0].min().min() if min_normal == 0: epsilon = df_normal_matched[df_normal_matched > 0].min().min() * 0.99 df_normal_matched[df_normal_matched == 0] = epsilon min_normal = epsilon # replace numerator values that are 0 with the next smallest non-zero value min_excluded = df_excluded_matched[df_excluded_matched > 0].min().min() if min_excluded == 0: epsilon = df_excluded_matched[df_excluded_matched > 0].min().min() * 0.99 df_excluded_matched[df_excluded_matched == 0] = epsilon min_excluded = epsilon # divide excluded by normal for the logfold change res = df_excluded_matched / df_normal_matched res[res == np.inf] = 1 res[res == -np.inf] = 1 # take the log2 of valid values res = np.log2(res) # save the new result to a file res.to_excel(out_loc + r"\example-logfold.xlsx", index=True)
示例数据
正常矩阵:
| Node | ACKR2 | ACKR3 |
|---|---|---|
| GNAI2 | 0.67 | 0.59 |
| GNAQ | 0 | 0 |
排除后矩阵:
| Node | ACKR2 | ACKR3 |
|---|---|---|
| GNAI2 | 0 | 0 |
| GNAQ | 0 | 0 |
错误排查与修正
你的代码存在以下几个关键问题:
1. 零值替换逻辑错误
- 当计算
min_normal时,如果原矩阵中存在非零值,min_normal是正数,但你的判断条件是if min_normal == 0:,这会导致非零矩阵中的零值根本不会被替换。比如示例中df_normal_matched的GNAQ行都是0,但因为min_normal是0.59(非零),所以不会进入替换分支,后续计算时分母为0的问题没解决。 - 对于
df_excluded_matched,示例中所有值都是0,df_excluded_matched[df_excluded_matched > 0].min().min()会返回NaN,此时min_excluded == 0的判断不成立,零值也不会被替换,分子为0的问题依然存在。
2. 未处理列的匹配问题
你只处理了行(index)的交集,但两个矩阵可能存在列差异,需要同时匹配行和列的交集,否则实际场景中会出现列不匹配导致的计算错误。
3. 除零后的inf处理时机错误
你先做除法再替换inf,但此时如果分母为0,除法结果是inf,替换为1后再取log2得到0,但按照规则,应该先处理零值再做除法,从根源上避免出现inf。
修正后的代码
import pandas as pd import numpy as np np.seterr(divide='ignore') # 输入输出路径 in_loc = r"C:\Users\..\Downloads" out_loc = r"C:\Users\..\Downloads" # 读取数据并设置索引 df_normal = pd.read_excel(in_loc + r"\example-normal.xlsx").set_index("Node") df_excluded = pd.read_excel(in_loc + r"\example-excluded.xlsx").set_index("Node") # 同时匹配行和列的交集,确保维度完全一致 common_rows = df_normal.index.intersection(df_excluded.index) common_cols = df_normal.columns.intersection(df_excluded.columns) df_normal_matched = df_normal.loc[common_rows, common_cols].copy() df_excluded_matched = df_excluded.loc[common_rows, common_cols].copy() # 处理分母(normal矩阵)的零值:用最小非零值*0.99替换,若无则设为极小值1e-9兜底 non_zero_normal = df_normal_matched[df_normal_matched > 0] if not non_zero_normal.empty: epsilon_normal = non_zero_normal.min().min() * 0.99 else: epsilon_normal = 1e-9 df_normal_matched.replace(0, epsilon_normal, inplace=True) # 处理分子(excluded矩阵)的零值:用最小非零值*0.99替换,若无则设为极小值1e-9兜底 non_zero_excluded = df_excluded_matched[df_excluded_matched > 0] if not non_zero_excluded.empty: epsilon_excluded = non_zero_excluded.min().min() * 0.99 else: epsilon_excluded = 1e-9 df_excluded_matched.replace(0, epsilon_excluded, inplace=True) # 计算比值并处理极端异常值 ratio = df_excluded_matched / df_normal_matched ratio.replace([np.inf, -np.inf, np.nan], 1, inplace=True) # 计算log2 res = np.log2(ratio) # 保存结果 res.to_excel(out_loc + r"\example-logfold.xlsx", index=True)
示例数据的计算结果
修正后,示例数据的结果符合预期:
- GNAQ行:分子分母都替换为1e-9,比值为1,
log2(1)=0 - GNAI2行:分子替换为1e-9,分母用原非零值,得到合理的负log2结果,无inf值
内容的提问来源于stack exchange,提问作者Scranton_Angler
相关产品推荐
相关产品推荐

