5D Library › 早期经验、表观遗传甲基化与跨代行为先验的传递边界 · English

第五章 差异易感性:兰花、蒲公英与交叉点检验

两种模型

精神病理学长期使用素质—应激模型:一些人带有某种易损的「素质」(遗传、气质或生理特点),在不利环境中更容易出问题;在良好环境中,他们与其他人没有区别。贝尔斯基与普吕斯提出了另一种可能:同样的特点也许代表的不是「易损」,而是对环境更敏感,无论环境是好是坏。在不利环境中,这些孩子表现更差;在支持性的环境中,他们表现得比其他孩子更好。这就是差异易感性(differential susceptibility)假说 (Belsky and Pluess 2009)。

证据

生物学的环境敏感性。 博伊斯与埃利斯从进化发展的角度提出「对情境的生物学敏感性」理论:应激反应性高的孩子在高度紧张的环境中更容易出现健康与行为问题,但在高度支持的环境中健康结局最好;他们推测,高反应性在极端不利与极端保护的环境中都更常见 (Boyce and Ellis 2005)。埃利斯与博伊斯后来借用瑞典语中「蒲公英孩子」(maskrosbarn,在几乎任何环境中都能生长)的说法,并与之相对地提出「兰花孩子」(在忽视中凋零、在精心照料下绽放),用这一比喻概括这一思路 (Ellis and Boyce 2008)。

贝尔斯基与普吕斯综述了气质(例如负性情绪性较高的婴儿)、生理(应激反应性)与若干候选基因(例如多巴胺 D4 受体与血清素转运体的某些变异)作为易感性标记的证据 (Belsky and Pluess 2009)。这一思路影响很大,因为它把「问题孩子」重新描述为「对环境最敏感的孩子」:他们不是注定出问题,而是最能从好的环境中获益。

交叉点:两种模型的统计区别

推导

设 Y=b0+b1E+b2S+b3ES+εY = b_0 + b_1 E + b_2 S + b_3 E S + \varepsilon,EE 为照料环境的质量(越高越好),SS 为易感性标记,YY 为适应结局(越高越好),都已标准化。高 SS 与低 SS 两条回归线在 Ec=−b2/b3E_c = -b_2 / b_3 处相交。差异易感性预测交叉点位于环境范围的中间;素质—应激预测交叉点位于或超出环境范围的上端。仿照罗伊斯曼等人的建议 (Roisman et al. 2012),程序报告:交叉点及其 95% 置信区间(德尔塔法);SS 的作用显著的 EE 区间(显著性区域);以及 PoI,即 S=+1S = +1 与 S=−1S = -1 两条线之间的面积(EE 从 −2 到 +2)中,落在「高 SS 更好」一侧的比例。系数为示意值,每种情形 400 名儿童。

# Differential susceptibility (DS) versus diathesis-stress (D-S).
# Y = b0 + b1 E + b2 S + b3 E*S + noise; E = caregiving quality, S = a
# susceptibility marker, Y = adjustment (higher = better), all standardised.
# High- and low-S lines cross at E_c = -b2 / b3: mid-range under DS, at or
# beyond the top of the range under D-S. Diagnostics after Roisman et al.
# (2012): crossover with 95 % CI (delta method), region of significance of
# S, and PoI = share of the area between the S = +1 and S = -1 lines (E from
# -2 to +2) on the "high S does better" side. Coefficients ILLUSTRATIVE.
import math, random

def fit(E, S, Y):                     # OLS via Gauss-Jordan: b and covariance
    X = [(1.0, e, s, e * s) for e, s in zip(E, S)]
    M = [[sum(x[i] * x[j] for x in X) for j in range(4)]
         + [sum(x[i] * y for x, y in zip(X, Y))]
         + [float(i == j) for j in range(4)] for i in range(4)]
    for c in range(4):
        p = max(range(c, 4), key=lambda r: abs(M[r][c])); M[c], M[p] = M[p], M[c]
        M[c] = [v / M[c][c] for v in M[c]]
        for r in (r for r in range(4) if r != c):
            M[r] = [a - M[r][c] * b for a, b in zip(M[r], M[c])]
    b = [M[i][4] for i in range(4)]
    s2 = sum((y - sum(u * w for u, w in zip(b, x))) ** 2
             for x, y in zip(X, Y)) / (len(Y) - 4)
    return b, [[s2 * M[i][5 + j] for j in range(4)] for i in range(4)]

def diagnose(b, V):
    b2, b3 = b[2], b[3]; ec = -b2 / b3
    se = math.sqrt(V[2][2] + ec * ec * V[3][3] + 2 * ec * V[2][3]) / abs(b3)
    z = lambda e: (b2 + b3 * e) / math.sqrt(V[2][2] + 2 * e * V[2][3]
                                            + e * e * V[3][3])
    sig = [abs(z(e / 100)) > 1.96 for e in range(-300, 301)]
    edges = [(e - 300) / 100 for e in range(1, 601) if sig[e] != sig[e - 1]]
    pos = sum(max(0, b2 + b3 * e / 100) for e in range(-200, 201))
    tot = sum(abs(b2 + b3 * e / 100) for e in range(-200, 201))
    return ec, se, edges, pos / tot

def data(kind, n, rng):
    E = [rng.gauss(0, 1) for _ in range(n)]; S = [rng.gauss(0, 1) for _ in range(n)]
    b = (0, 0.30, 0.0, 0.25) if kind == "DS" else (0, 0.30, -0.25, 0.125)
    Y = [b[1] * e + b[2] * s + b[3] * e * s + rng.gauss(0, 1) for e, s in zip(E, S)]
    return E, S, Y, b

if __name__ == "__main__":
    rng = random.Random(2009)
    for kind in ("DS", "D-S"):
        E, S, Y, tr = data(kind, 400, rng)
        b, V = fit(E, S, Y); ec, se, edges, poi = diagnose(b, V)
        z3 = b[3] / math.sqrt(V[3][3])
        print(f"{kind}: true crossover {-tr[2] / tr[3] + 0.0:+.1f} SD; n = 400;"
              f" b2 = {b[2]:+.3f}, b3 = {b[3]:.3f} (z = {z3:.2f})")
        print(f"  crossover {ec:+.2f} SD (95 % CI {ec - 1.96 * se:+.2f} to"
              f" {ec + 1.96 * se:+.2f})")
        print("  significance of S switches at E = "
              + ", ".join(f"{x:+.2f}" for x in edges) + "  (scan from -3 to +3)")
        print(f"  PoI = {poi:.2f}; sample above the crossover:"
              f" {sum(e > ec for e in E) / len(E):.0%}")
# DS: true crossover +0.0 SD; n = 400; b2 = +0.041, b3 = 0.224 (z = 5.04)
#   crossover -0.18 SD (95 % CI -0.61 to +0.25)
#   significance of S switches at E = -0.66, +0.27  (scan from -3 to +3)
#   PoI = 0.59; sample above the crossover: 56%
# D-S: true crossover +2.0 SD; n = 400; b2 = -0.239, b3 = 0.125 (z = 2.33)
#   crossover +1.92 SD (95 % CI +0.15 to +3.69)
#   significance of S switches at E = +0.87  (scan from -3 to +3)
#   PoI = 0.00; sample above the crossover: 5%
两种模式的拟合回归线(程序 p10 的模拟数据)。左:差异易感性,两条线在环境中部相交,高易感性儿童在差的环境中更差、在好的环境中更好;右:素质—应激,交叉点位于环境范围的上端,高易感性儿童只是在最好的环境中才赶上其他儿童。点线为估计的交叉点。

在差异易感性情形中,估计的交叉点为 −0.18 个标准差(95% 置信区间 −0.61 至 +0.25),位于环境分布的中部;SS 在交叉点两侧都有显著作用(EE 低于约 −0.66 时高 SS 更差,高于约 +0.27 时高 SS 更好);PoI 为 0.59,约 56% 的样本位于交叉点之上。在素质—应激情形中,交叉点为 +1.92(95% 置信区间 +0.15 至 +3.69),只有 5% 的样本位于其上;SS 只在 EE 低于约 +0.87 时显著;PoI 为 0。

两种模式都给出了显著的交互项(zz 分别为 5.04 与 2.33),所以只报告交互项显著不足以支持差异易感性;必须检查交叉点的位置与「为了更好」那一侧是否真的存在。还要注意素质—应激情形中交叉点的置信区间非常宽,从 +0.15 一直到 +3.69:即使有 400 名儿童,交叉点的位置也很难精确估计。

注

环境范围决定能看到什么。 如果样本中几乎没有真正高质量的照料环境,「为了更好」的那一半就无从观察,差异易感性会看起来像素质—应激;反过来,如果测量的「环境」主要是不利的一端,也看不到上端。这也是为什么罗伊斯曼等人建议把交叉点与观察到的环境范围(例如 ±2 个标准差)对照着解释。

检验力与过拟合

推导

第一部分:真实的差异易感性,交互作用较小(b3=0.10b_3 = 0.10)。一项研究有多大机会(a)得到显著的交互项,(b)满足完整的差异易感性模式:交互项显著、交叉点的 95% 置信区间落在 ±2 个标准差之内、而且 SS 在 E=−2E = -2 与 E=+2E = +2 处的作用都显著且方向相反?第二部分:10 个候选标记,没有一个真正起调节作用,每项研究 200 名儿童。

# Power for E x S interactions, and the cost of testing many candidate markers.
# Part 1: true DS with a SMALL interaction, b3 = 0.10 (b1 = 0.30, b2 = 0). How
#   often does a study (a) find p < .05 for b3, (b) meet the full DS pattern:
#   significant b3, crossover 95 % CI inside -2..+2 SD, and significant
#   effects of S of opposite sign at E = -2 and E = +2?
# Part 2: 10 candidate markers, none of which moderates anything, n = 200.
# Coefficients ILLUSTRATIVE; E, S standard normal; residual SD 1.
import math, random
rng = random.Random(1993)

def fit(E, S, Y):                     # OLS via Gauss-Jordan: b and covariance
    X = [(1.0, e, s, e * s) for e, s in zip(E, S)]
    M = [[sum(x[i] * x[j] for x in X) for j in range(4)]
         + [sum(x[i] * y for x, y in zip(X, Y))]
         + [float(i == j) for j in range(4)] for i in range(4)]
    for c in range(4):
        p = max(range(c, 4), key=lambda r: abs(M[r][c])); M[c], M[p] = M[p], M[c]
        M[c] = [v / M[c][c] for v in M[c]]
        for r in (r for r in range(4) if r != c):
            M[r] = [a - M[r][c] * b for a, b in zip(M[r], M[c])]
    b = [M[i][4] for i in range(4)]
    s2 = sum((y - sum(u * w for u, w in zip(b, x))) ** 2
             for x, y in zip(X, Y)) / (len(Y) - 4)
    return b, [[s2 * M[i][5 + j] for j in range(4)] for i in range(4)]

def ds_pattern(b, V):
    b2, b3 = b[2], b[3]
    if abs(b3) / math.sqrt(V[3][3]) < 1.96: return False, False
    ec = -b2 / b3
    se = math.sqrt(V[2][2] + ec * ec * V[3][3] + 2 * ec * V[2][3]) / abs(b3)
    z = lambda e: (b2 + b3 * e) / math.sqrt(V[2][2] + 2 * e * V[2][3]
                                            + e * e * V[3][3])
    both = abs(z(-2)) > 1.96 and abs(z(2)) > 1.96 and z(-2) * z(2) < 0
    return True, -2 < ec - 1.96 * se and ec + 1.96 * se < 2 and both

def study(n, b3):
    E = [rng.gauss(0, 1) for _ in range(n)]; S = [rng.gauss(0, 1) for _ in range(n)]
    Y = [0.30 * e + b3 * e * s + rng.gauss(0, 1) for e, s in zip(E, S)]
    return ds_pattern(*fit(E, S, Y))

print("true b3 = 0.10 (DS, crossover at 0): 300 simulated studies per size")
print("     n  | b3 significant | full DS pattern")
for n in (100, 300, 1000):
    res = [study(n, 0.10) for _ in range(300)]
    a = sum(x for x, _ in res) / 300; c = sum(y for _, y in res) / 300
    print(f"  {n:5d} | {a:14.0%} | {c:15.0%}")
need = math.ceil(((1.96 + 1.2816) / 0.10) ** 2)        # se(b3) ~ 1/sqrt(n)
print(f"normal approximation: about {need} children for 90 % power")
hits, sizes = 0, []
for _ in range(300):
    E = [rng.gauss(0, 1) for _ in range(200)]
    Y = [0.30 * e + rng.gauss(0, 1) for e in E]
    found = False
    for m in range(10):
        b, V = fit(E, [rng.gauss(0, 1) for _ in range(200)], Y)
        if abs(b[3]) / math.sqrt(V[3][3]) > 1.96:
            found = True; sizes.append(abs(b[3]))
    hits += found
print(f"10 null markers, n = 200: a 'significant' interaction in {hits / 300:.0%}"
      " of studies;")
print(f"  these average |b3| = {sum(sizes) / len(sizes):.2f}, though every"
      " true b3 is 0")
# true b3 = 0.10 (DS, crossover at 0): 300 simulated studies per size
#      n  | b3 significant | full DS pattern
#     100 |            18% |              6%
#     300 |            43% |             23%
#    1000 |            89% |             69%
# normal approximation: about 1051 children for 90 % power
# 10 null markers, n = 200: a 'significant' interaction in 43% of studies;
#   these average |b3| = 0.17, though every true b3 is 0

当真实的交互作用为 0.10 时,100 名儿童的研究只有约 18% 的机会检出交互项,满足完整模式的只有约 6%;300 名儿童分别为 43% 与 23%;1,000 名儿童才达到 89% 与 69%。按正态近似,要以 90% 的把握度检出这样的交互作用,约需 1,051 名儿童。第二部分更值得警惕:在 10 个完全无效的候选标记中,约 43% 的研究会至少找到一个「显著」的交互作用,而这些「显著」交互作用的平均大小约为 0.17,比上面那个真实存在的交互作用还大。

这就是候选基因研究在过去二十年中遇到的问题的缩影。

证据

候选基因交互作用的重复危机。 邓肯与凯勒回顾了精神病学中候选基因与环境交互作用研究的前十年:首次发表的研究 96% 报告了显著结果,而重复研究只有 27% 显著,并且存在明显的发表偏倚 (Duncan and Keller 2011)。博德等人在多个大样本(最大达数十万人)中检验了 18 个历史上被研究最多的抑郁症候选基因及其与环境的交互作用,没有找到支持 (Border et al. 2019)。

所以,「携带某种基因变异的孩子是兰花」这类说法目前没有可靠依据。差异易感性作为一种模式仍值得研究,部分随机实验(比较干预对不同易感性儿童的效果)与基于全基因组或行为与生理测量的研究提供了一些支持,但效应通常不大,重复也不充分。

这对家庭意味着什么

注

不要给孩子贴「兰花」标签。 差异易感性是一个关于人群中交互作用模式的假说,目前不能用任何测试来判断某个孩子是「兰花」还是「蒲公英」,更不能据此决定怎样对待他。它对父母的一个合理启示是温和的:同样的家庭环境对不同的孩子可能有不同的影响,兄弟姐妹之间的差异不一定说明父母偏心或某个孩子「有问题」。这与第二章中较大的非共享环境也是一致的。

假说 · 尚未验证

易感性与「暂停示范」(尚未确立)。如果差异易感性成立,那么在父母学会压力下的暂停示范之后,最受益的应是对环境最敏感的孩子。可以检验的预测:在第八章的试验中,干预效果在基线时情绪反应性较高的儿童中更大,而且在对照组中这些儿童的结局并不更好;交叉点应落在观察到的范围之内。这一分析只能作为探索性分析预先登记;按本章的检验力计算,第八章的样本量不足以可靠检出这类交互作用。如果效果在高反应性儿童中不更大,或者高反应性儿童在对照组中表现更差而在干预组中只是赶上,后者更符合素质—应激模型。

本章小结

差异易感性认为同样的孩子「为了更好,也为了更坏」,素质—应激认为他们只在不利环境中更差。两者都产生显著的交互项,只有检查交叉点、显著性区域与 PoI 才能区分,而交叉点的置信区间往往很宽。小的交互作用需要上千名儿童;同时检验许多候选标记会产生大量夸大的假阳性,候选基因交互作用的重复率很低。下一章讨论什么能减弱负向的代际循环。

参考文献

Belsky, Jay, and Michael Pluess. 2009. “Beyond Diathesis Stress: Differential Susceptibility to Environmental Influences.” Psychological Bulletin 135 (6): 885–908. https://doi.org/10.1037/a0017376.
Border, Richard, Emma C. Johnson, Luke M. Evans, et al. 2019. “No Support for Historical Candidate Gene or Candidate Gene-by-Interaction Hypotheses for Major Depression Across Multiple Large Samples.” American Journal of Psychiatry 176 (5): 376–87. https://doi.org/10.1176/appi.ajp.2018.18070881.
Boyce, W. Thomas, and Bruce J. Ellis. 2005. “Biological Sensitivity to Context: I. An Evolutionary-Developmental Theory of the Origins and Functions of Stress Reactivity.” Development and Psychopathology 17 (2): 271–301. https://doi.org/10.1017/S0954579405050145.
Duncan, Laramie E., and Matthew C. Keller. 2011. “A Critical Review of the First 10 Years of Candidate Gene-by-Environment Interaction Research in Psychiatry.” American Journal of Psychiatry 168 (10): 1041–49. https://doi.org/10.1176/appi.ajp.2011.11020191.
Ellis, Bruce J., and W. Thomas Boyce. 2008. “Biological Sensitivity to Context.” Current Directions in Psychological Science 17 (3): 183–87. https://doi.org/10.1111/j.1467-8721.2008.00571.x.
Roisman, Glenn I., Daniel A. Newman, R. Chris Fraley, John D. Haltigan, Ashley M. Groh, and Katherine C. Haydon. 2012. “Distinguishing Differential Susceptibility from Diathesis-Stress: Recommendations for Evaluating Interaction Effects.” Development and Psychopathology 24 (2): 389–409. https://doi.org/10.1017/S0954579412000065.

Fan, Y. (2026). Early Experience, Epigenetic Methylation and the Limits of Intergenerational Transmission: From Glucocorticoid-Receptor Programming and Twin Variance Decomposition to Social-Learning Channels, Read Beside Classical Chinese Family Instruction [早期经验、表观遗传甲基化与跨代行为先验的传递边界] (Version 1.0.0). 5D Library. https://doi.org/10.5281/zenodo.23761337 · Licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0).