新增.hintrc配置文件,更新多个h5ad文件的大小和哈希值,修改FakeEmbryo.py中的细胞层参数,调整前端页面中细胞位置和大小的计算,优化TemporalAnalysis.tsx中的数据连接逻辑以确保细胞类型流动的平衡性。
This commit is contained in:
parent
801700cd3d
commit
c3e965b535
8
.hintrc
Normal file
8
.hintrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": [
|
||||
"development"
|
||||
],
|
||||
"hints": {
|
||||
"no-inline-styles": "off"
|
||||
}
|
||||
}
|
||||
BIN
embryo-backend/Data/CS7.5.h5ad
(Stored with Git LFS)
BIN
embryo-backend/Data/CS7.5.h5ad
(Stored with Git LFS)
Binary file not shown.
BIN
embryo-backend/Data/CS7.h5ad
(Stored with Git LFS)
BIN
embryo-backend/Data/CS7.h5ad
(Stored with Git LFS)
Binary file not shown.
BIN
embryo-backend/Data/CS8.h5ad
(Stored with Git LFS)
BIN
embryo-backend/Data/CS8.h5ad
(Stored with Git LFS)
Binary file not shown.
BIN
embryo-backend/Data/CS9.h5ad
(Stored with Git LFS)
BIN
embryo-backend/Data/CS9.h5ad
(Stored with Git LFS)
Binary file not shown.
@ -6,12 +6,12 @@ import os
|
||||
np.random.seed(42)
|
||||
|
||||
# 参数配置
|
||||
stages = [("CS7", 500), ("CS8", 1000), ("CS9", 1500)]
|
||||
stages = [("CS7", 500), ("CS7.5", 750), ("CS8", 1000), ("CS9", 1500)]
|
||||
genes = ["SOX2", "NANOG", "T", "POU5F1", "OTX2", "ZIC2", "FOXA2", "LEFTY1"]
|
||||
layers = {
|
||||
"Ectoderm": 1.0, # 外层
|
||||
"Mesoderm": 0.85, # 中层
|
||||
"Endoderm": 0.7, # 内层
|
||||
"Ectoderm": 0.1, # 外层
|
||||
"Mesoderm": 0.085, # 中层
|
||||
"Endoderm": 0.07, # 内层
|
||||
}
|
||||
notochord_ratio = 0.05 # 脊索/原条占总细胞数的比例
|
||||
|
||||
@ -21,17 +21,23 @@ script_path = os.path.dirname(os.path.realpath(__file__))
|
||||
for stage, total_cells in stages:
|
||||
n_noto = int(total_cells * notochord_ratio)
|
||||
n_layer = total_cells - n_noto
|
||||
n_per_layer = n_layer // len(layers)
|
||||
num_layers = len(layers)
|
||||
# 使用 Dirichlet 生成随机且不相等的概率,再用多项分布采样;若计数仍相等则重采样
|
||||
for _ in range(100):
|
||||
probs = np.random.dirichlet(np.ones(num_layers))
|
||||
n_per_layer = np.random.multinomial(n_layer, probs)
|
||||
if len(set(n_per_layer)) == num_layers:
|
||||
break
|
||||
|
||||
positions = []
|
||||
cell_types = []
|
||||
ids = []
|
||||
|
||||
# 椭球比例参数
|
||||
a, b, c = 10, 8, 5
|
||||
a, b, c = 1, 0.8, 0.5
|
||||
|
||||
for i, (layer_name, scale) in enumerate(layers.items()):
|
||||
N = n_per_layer
|
||||
N = n_per_layer[i]
|
||||
phi = np.random.uniform(0, np.pi, N)
|
||||
theta = np.random.uniform(0, 2*np.pi, N)
|
||||
r = scale # 层的半径比例
|
||||
@ -46,9 +52,9 @@ for stage, total_cells in stages:
|
||||
ids.append(f"{stage}_{layer_name}_{len(ids)}")
|
||||
|
||||
# 添加脊索/原条样结构(Z轴中间偏下的细胞)
|
||||
x = np.random.normal(0, 0.5, n_noto)
|
||||
y = np.random.normal(0, 0.5, n_noto)
|
||||
z = np.linspace(-3, 3, n_noto)
|
||||
x = np.random.normal(0, 0.01, n_noto)
|
||||
y = np.random.normal(0, 0.01, n_noto)
|
||||
z = np.linspace(-0.1, 0.1, n_noto)
|
||||
|
||||
for xi, yi, zi in zip(x, y, z):
|
||||
positions.append([xi, yi, zi])
|
||||
|
||||
@ -258,10 +258,10 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
|
||||
// 计算所有点的包围盒,自动居中和缩放
|
||||
let allPositions: number[] = [];
|
||||
cs7Data.forEach(p => { allPositions.push(p.x - 40, p.y, p.z); });
|
||||
cs8Data.forEach(p => { allPositions.push(p.x + 40, p.y, p.z); });
|
||||
cs7Data.forEach(p => { allPositions.push(p.x - 0.5, p.y, p.z); });
|
||||
cs8Data.forEach(p => { allPositions.push(p.x + 0.5,p.y, p.z); });
|
||||
cs75Data.forEach((p, i) => {
|
||||
const fromX = i % 2 === 0 ? p.x - 40 : p.x + 40;
|
||||
const fromX = i % 2 === 0 ? p.x - 0.5 : p.x + 0.5;
|
||||
allPositions.push(fromX, p.y, p.z);
|
||||
allPositions.push(p.x, p.y, p.z);
|
||||
});
|
||||
@ -285,7 +285,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
const positions: number[] = [];
|
||||
const colors: number[] = [];
|
||||
cs7Data.forEach((p) => {
|
||||
positions.push(p.x - 40, p.y, p.z);
|
||||
positions.push(p.x - 0.5, p.y, p.z);
|
||||
const c = colorMap.get(p.value as string) || new THREE.Color('#CCCCCC');
|
||||
colors.push(c.r, c.g, c.b);
|
||||
});
|
||||
@ -293,7 +293,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({
|
||||
vertexColors: true,
|
||||
size: 0.8,
|
||||
size: 0.4,
|
||||
sizeAttenuation: true,
|
||||
transparent: true,
|
||||
alphaTest: 0.5,
|
||||
@ -327,7 +327,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
const positions: number[] = [];
|
||||
const colors: number[] = [];
|
||||
cs8Data.forEach((p) => {
|
||||
positions.push(p.x + 40, p.y, p.z);
|
||||
positions.push(p.x + 0.5, p.y, p.z);
|
||||
const c = colorMap.get(p.value as string) || new THREE.Color('#CCCCCC');
|
||||
colors.push(c.r, c.g, c.b);
|
||||
});
|
||||
@ -335,7 +335,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({
|
||||
vertexColors: true,
|
||||
size: 0.8,
|
||||
size: 0.4,
|
||||
sizeAttenuation: true,
|
||||
transparent: true,
|
||||
alphaTest: 0.5,
|
||||
@ -408,7 +408,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
const sourcePoint = cs7PointsOfType.length > 0
|
||||
? cs7PointsOfType[sourceIndex]
|
||||
: cs7Data[i % cs7Data.length];
|
||||
fromX = sourcePoint.x - 40;
|
||||
fromX = sourcePoint.x - 0.5;
|
||||
fromY = sourcePoint.y;
|
||||
fromZ = sourcePoint.z;
|
||||
} else {
|
||||
@ -418,7 +418,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
const sourcePoint = cs8PointsOfType.length > 0
|
||||
? cs8PointsOfType[sourceIndex]
|
||||
: cs8Data[i % cs8Data.length];
|
||||
fromX = sourcePoint.x + 40;
|
||||
fromX = sourcePoint.x + 0.5;
|
||||
fromY = sourcePoint.y;
|
||||
fromZ = sourcePoint.z;
|
||||
}
|
||||
@ -466,7 +466,7 @@ const UnifiedEmbryoAnimation: React.FC<UnifiedEmbryoAnimationProps> = ({ cs7Data
|
||||
vAlpha = alpha;
|
||||
vColor = color;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
gl_PointSize = 10.0;
|
||||
gl_PointSize = 5.0;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@ -184,39 +184,79 @@ const TemporalAnalysis: React.FC = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 构建连接
|
||||
const links: any[] = [];
|
||||
cellTypeArray.forEach(cellType => {
|
||||
const color = colorMap.get(cellType) || '#CCCCCC';
|
||||
// 构建连接:使用最小百分比作为同类连线,并将剩余部分匹配到有缺口的目标细胞类型,保证各阶段总量守恒
|
||||
const buildBalancedLinks = (
|
||||
sourceCounts: Record<string, number>,
|
||||
sourceTotal: number,
|
||||
targetCounts: Record<string, number>,
|
||||
targetTotal: number,
|
||||
sourceStage: string,
|
||||
targetStage: string
|
||||
) => {
|
||||
const types = Array.from(
|
||||
new Set([
|
||||
...Object.keys(sourceCounts),
|
||||
...Object.keys(targetCounts)
|
||||
])
|
||||
);
|
||||
|
||||
// CS7 -> CS8 连接
|
||||
if (countsCS7[cellType] && countsCS8[cellType]) {
|
||||
const cs7Percentage = (countsCS7[cellType] / totalCS7) * 100;
|
||||
const cs8Percentage = (countsCS8[cellType] / totalCS8) * 100;
|
||||
const avgPercentage = (cs7Percentage + cs8Percentage) / 2;
|
||||
const sourcePct: Record<string, number> = {};
|
||||
const targetPct: Record<string, number> = {};
|
||||
types.forEach((t) => {
|
||||
sourcePct[t] = ((sourceCounts[t] || 0) / sourceTotal) * 100;
|
||||
targetPct[t] = ((targetCounts[t] || 0) / targetTotal) * 100;
|
||||
});
|
||||
|
||||
links.push({
|
||||
source: `CS7_${cellType}`,
|
||||
target: `CS8_${cellType}`,
|
||||
value: Math.max(avgPercentage, 0.1), // 确保有最小值以显示连接
|
||||
color: color + '80' // 添加透明度
|
||||
const flows: any[] = [];
|
||||
|
||||
// 同类对齐:取最小值
|
||||
const sourceResidual: Record<string, number> = {};
|
||||
const targetResidual: Record<string, number> = {};
|
||||
types.forEach((t) => {
|
||||
const direct = Math.min(sourcePct[t], targetPct[t]);
|
||||
if (direct > 0) {
|
||||
const color = (colorMap.get(t) || '#CCCCCC') + '80';
|
||||
flows.push({
|
||||
source: `${sourceStage}_${t}`,
|
||||
target: `${targetStage}_${t}`,
|
||||
value: direct,
|
||||
color
|
||||
});
|
||||
}
|
||||
|
||||
// CS8 -> CS9 连接
|
||||
if (countsCS8[cellType] && countsCS9[cellType]) {
|
||||
const cs8Percentage = (countsCS8[cellType] / totalCS8) * 100;
|
||||
const cs9Percentage = (countsCS9[cellType] / totalCS9) * 100;
|
||||
const avgPercentage = (cs8Percentage + cs9Percentage) / 2;
|
||||
|
||||
links.push({
|
||||
source: `CS8_${cellType}`,
|
||||
target: `CS9_${cellType}`,
|
||||
value: Math.max(avgPercentage, 0.1), // 确保有最小值以显示连接
|
||||
color: color + '80' // 添加透明度
|
||||
sourceResidual[t] = sourcePct[t] - direct; // >= 0
|
||||
targetResidual[t] = targetPct[t] - direct; // >= 0
|
||||
});
|
||||
|
||||
// 将源阶段的剩余按目标阶段的缺口比例分配
|
||||
types.forEach((sType) => {
|
||||
let remaining = sourceResidual[sType];
|
||||
if (remaining <= 0) return;
|
||||
// 计算当前目标缺口总和
|
||||
let totalDeficit = types.reduce((sum, t) => sum + Math.max(targetResidual[t], 0), 0);
|
||||
if (totalDeficit <= 0) return;
|
||||
types.forEach((dType) => {
|
||||
if (targetResidual[dType] <= 0) return;
|
||||
const share = (remaining * targetResidual[dType]) / totalDeficit;
|
||||
if (share > 1e-6) {
|
||||
const color = (colorMap.get(sType) || '#CCCCCC') + '80';
|
||||
flows.push({
|
||||
source: `${sourceStage}_${sType}`,
|
||||
target: `${targetStage}_${dType}`,
|
||||
value: share,
|
||||
color
|
||||
});
|
||||
targetResidual[dType] -= share;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return flows;
|
||||
};
|
||||
|
||||
const links: any[] = [
|
||||
...buildBalancedLinks(countsCS7, totalCS7, countsCS8, totalCS8, 'CS7', 'CS8'),
|
||||
...buildBalancedLinks(countsCS8, totalCS8, countsCS9, totalCS9, 'CS8', 'CS9')
|
||||
];
|
||||
|
||||
setSankeyData({ nodes, links });
|
||||
} catch (error) {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user