Buckets:

redmingwei's picture
download
raw
7.62 kB
#!/usr/bin/env python3
"""exp49b — tau 的规范依赖与噪声底污染(spike, 20 ms)。
上一步发现:逐成分 ACF 取中位数,量到的是噪声。
churchland_00 top-5 特征值 = 0.31, 0.047, 0.029, 0.013, 0.011
PC1 ACF(1) = 0.87,而全部成分 ACF 中位数 = 0.055
所以 τ 的小值不是「数据没有相关性」,而是「中位数被 63 个噪声成分主导」。
本脚本比较五个量:
T1 median_PC 逐成分 ACF 取中位数(旧方法)
T2 mean_PC 逐成分 ACF 等权平均(我上一版的错误)
T3 trace_weighted tr C(τ) / tr C(0) —— 方差加权,且正交不变
T4 PC1_only 只用第 1 主成分
T5 sobi_median 联合对角化后的成分取中位数
并检验:
R1 T1 在随机正交旋转下是否改变 -> 变了说明是基的产物
R2 T3 在随机正交旋转下是否改变 -> 应保持到 1e-6
"""
from __future__ import annotations
import json
import time
from pathlib import Path
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
SEG = Path("/domains/primate/macaque_nlb")
OUT = Path("/domains/runs/tau-gauge")
OUT.mkdir(parents=True, exist_ok=True)
DT, MAX_LAG, N_ROT, SEED = 20.0, 100, 5, 0
def acf_cols(x, max_lag):
T = x.shape[0]
xc = x - x.mean(axis=0, keepdims=True)
n = 1
while n < 2 * T:
n <<= 1
f = np.fft.rfft(xc, n=n, axis=0)
acf = np.fft.irfft(f * np.conj(f), n=n, axis=0)[:max_lag + 1]
norms = np.arange(T, T - max_lag - 1, -1)[:, None]
acf = acf / np.maximum(norms, 1)
return acf / np.maximum(acf[0:1], 1e-12)
def fit_tau(a, dt=DT):
ok = np.isfinite(a) & (a > 0.02) & (a < 0.995)
if ok.sum() < 5:
return None
t = np.arange(len(a))[ok] * dt
y = a[ok]
def f(tt, T):
return np.exp(-tt / np.maximum(T, 1e-3))
try:
popt, _ = curve_fit(f, t, y, p0=[200.0], bounds=([1.0], [50000.0]), maxfev=8000)
return float(popt[0])
except Exception:
return None
def weighted_acf(x, max_lag):
"""tr C(τ) / tr C(0),按方差加权。正交不变。"""
T = x.shape[0]
xc = x - x.mean(axis=0, keepdims=True)
n = 1
while n < 2 * T:
n <<= 1
f = np.fft.rfft(xc, n=n, axis=0)
acf = np.fft.irfft(f * np.conj(f), n=n, axis=0)[:max_lag + 1] # (lag, d)
norms = np.arange(T, T - max_lag - 1, -1)[:, None]
acf = acf / np.maximum(norms, 1)
return acf.sum(axis=1) / max(acf[0].sum(), 1e-12)
def sobi(x):
T = x.shape[0]
C0 = x.T @ x / T
C1 = x[:-1].T @ x[1:] / (T - 1)
C0 = (C0 + C0.T) / 2
try:
w, V = np.linalg.eigh(np.linalg.solve(C0, (C1 + C1.T) / 2))
except np.linalg.LinAlgError:
return None
return V[:, np.argsort(-np.abs(w))]
def main():
t0 = time.time()
rng = np.random.default_rng(SEED)
rows = []
files = sorted(SEG.glob("spikes_*.npz"))
print(f"{len(files)} segments", flush=True)
for k, f in enumerate(files):
raw = np.load(f)["spikes"].astype(np.float32) # (n_units, n_bins20)
if raw.shape[1] < MAX_LAG + 50 or raw.shape[0] < 10:
continue
mu = raw.mean(axis=1, keepdims=True)
sd = raw.std(axis=1, keepdims=True) + 1e-6
zc = ((raw - mu) / sd)
zc = zc - zc.mean(axis=1, keepdims=True)
C = zc @ zc.T
w, U = np.linalg.eigh(C)
o = np.argsort(-w)
w, U = np.clip(w[o], 0, None), U[:, o]
d = min(64, zc.shape[0] - 1, zc.shape[1] - 1)
proj = zc.T @ U[:, :d] # (n_bins, d) 方差 = 特征值
ml = min(MAX_LAG, proj.shape[0] - 2)
A = acf_cols(proj, ml)
t1 = fit_tau(np.median(A, axis=1))
t2 = fit_tau(A.mean(axis=1))
t3 = fit_tau(weighted_acf(proj, ml))
t4 = fit_tau(A[:, 0])
V = sobi(proj)
t5 = fit_tau(np.median(acf_cols(proj @ V, ml), axis=1)) if V is not None else None
# 旋转检验
r1, r3 = [], []
for _ in range(N_ROT):
Q, _ = np.linalg.qr(rng.standard_normal((d, d)))
Pq = proj @ Q
r1.append(fit_tau(np.median(acf_cols(Pq, ml), axis=1)))
r3.append(fit_tau(weighted_acf(Pq, ml)))
r1 = [v for v in r1 if v]
r3 = [v for v in r3 if v]
rows.append({
"file": f.name,
"n_units": int(raw.shape[0]),
"n_bins": int(raw.shape[1]),
"d": int(d),
"ev_top1": float(w[0] / max(w[:d].sum(), 1e-12)),
"ev_top5": float(w[:5].sum() / max(w[:d].sum(), 1e-12)),
"rate": float(raw.mean()),
"tau_T1_medianPC": t1,
"tau_T2_meanPC": t2,
"tau_T3_weighted": t3,
"tau_T4_PC1": t4,
"tau_T5_sobi": t5,
"rot_T1_min": float(np.min(r1)) if r1 else None,
"rot_T1_max": float(np.max(r1)) if r1 else None,
"rot_T3_min": float(np.min(r3)) if r3 else None,
"rot_T3_max": float(np.max(r3)) if r3 else None,
"rot_T3_spread": float(np.ptp(r3)) if r3 else None,
"acf1_pc1": float(A[1, 0]),
"acf1_median": float(np.median(A[1])),
})
if k % 10 == 0:
print(f" [{k+1}/{len(files)}] {f.name} d={d} "
f"T1={t1} T3={t3} PC1={t4} [{time.time()-t0:.0f}s]", flush=True)
df = pd.DataFrame(rows)
df.to_csv(OUT / "results_spike.csv", index=False)
def s(c):
v = df[c].dropna()
return {"n": int(len(v)), "median": round(float(v.median()), 1),
"p10": round(float(v.quantile(.1)), 1),
"p90": round(float(v.quantile(.9)), 1)}
rep = {
"task": "tau_gauge_spike_20ms",
"dt_ms": DT, "n_segments": int(len(df)),
"ev_top1_median": round(float(df.ev_top1.median()), 4),
"tau": {k: s(f"tau_{k}") for k in
["T1_medianPC", "T2_meanPC", "T3_weighted", "T4_PC1", "T5_sobi"]},
"acf1": {"pc1_median": round(float(df.acf1_pc1.median()), 4),
"allPC_median": round(float(df.acf1_median.median()), 4)},
"rotation_T1": {
"n": int(df.rot_T1_min.notna().sum()),
"median_ratio_max_over_min": round(float(
(df.rot_T1_max / df.rot_T1_min).replace([np.inf], np.nan).dropna().median()), 3),
"max_ratio": round(float(
(df.rot_T1_max / df.rot_T1_min).replace([np.inf], np.nan).dropna().max()), 3),
},
"rotation_T3": {
"n": int(df.rot_T3_spread.notna().sum()),
"median_abs_spread_ms": round(float(df.rot_T3_spread.median()), 4),
"max_abs_spread_ms": round(float(df.rot_T3_spread.max()), 4),
},
}
from scipy.stats import spearmanr
m = df[["tau_T1_medianPC", "tau_T3_weighted", "tau_T4_PC1", "ev_top1"]].dropna()
if len(m) > 5:
rep["rank_corr"] = {
"T1_vs_T3": round(float(spearmanr(m.tau_T1_medianPC, m.tau_T3_weighted)[0]), 3),
"T1_vs_PC1": round(float(spearmanr(m.tau_T1_medianPC, m.tau_T4_PC1)[0]), 3),
"T3_vs_PC1": round(float(spearmanr(m.tau_T3_weighted, m.tau_T4_PC1)[0]), 3),
"ev_top1_vs_T1": round(float(spearmanr(m.ev_top1, m.tau_T1_medianPC)[0]), 3),
"ev_top1_vs_PC1": round(float(spearmanr(m.ev_top1, m.tau_T4_PC1)[0]), 3),
"n": int(len(m)),
}
(OUT / "report_spike.json").write_text(json.dumps(rep, indent=2), encoding="utf-8")
print("\n" + json.dumps(rep, indent=2), flush=True)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
7.62 kB
·
Xet hash:
ea141e1a4d2622f834c9789be83848662308bcd4e4494cff221c990ff423875a

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.