4강 / 20강

96%가 0이다 — 희소행렬 삼총사를 읽는다

data · indices · indptr 이 어떻게 행렬이 되는가

96%가 0이다 — 희소행렬 삼총사를 읽는다
칸의 96%가 0입니다. 0을 저장하지 않는 것이 단일세포 자료를 다루는 유일한 방법입니다.

앞 강의에서 data · indices · indptr 이라는 낯선 이름 셋을 봤습니다. 이 셋이 CSC(Compressed Sparse Column) 형식입니다. 이름은 무섭지만 발상은 아주 간단해요.

0은 적지 않는다. 0이 아닌 값만 적고, 그것이 어디 있었는지를 따로 적는다.

작은 예로 보겠습니다. 유전자 5개 × 세포 3개짜리 행렬이 이렇게 생겼다고 합시다.

세포0 세포1 세포2 유전자0 0 0 4 유전자1 3 0 0 유전자2 0 7 0 유전자3 0 2 1 유전자4 5 0 0

0이 아닌 값은 여섯 개뿐입니다. 열(세포) 순서대로 훑으며 적어 봅시다.

data = [3, 5, 7, 2, 4, 1] ← 0이 아닌 값들, 열 순서대로 indices = [1, 4, 2, 3, 0, 3] ← 그 값이 몇 번째 행(유전자)이었나 indptr = [0, 2, 4, 6] ← 각 열이 data 의 몇 번째에서 시작하나

indptr 이 핵심입니다. 세포 k 의 값은 data[indptr[k] : indptr[k+1]] 에 있다 — 이 한 줄이 전부예요. 세포 1을 보고 싶으면 data[2:4] = [7, 2], 그 행 번호는 indices[2:4] = [2, 3]. 즉 유전자2에 7, 유전자3에 2. 원래 행렬과 일치합니다.

작은 행렬의 칸을 눌러 값을 바꿔 보세요. data · indices · indptr 세 배열이 실시간으로 다시 만들어집니다.
불러오는 중…
직접 만들어 보면 삼총사가 하나도 신비롭지 않습니다.
Python 브라우저에서 실행
3.8% 만 채워져 있습니다. 나머지 96.2% 는 전부 0 이에요.
Python 브라우저에서 실행

0이 이렇게 많은 것이 정상인가요?

네, 정상입니다. 그리고 그 0에는 두 가지 다른 의미가 섞여 있습니다.

진짜 0 — 그 유전자가 안 켜졌다

T세포에서 S100a8(호중구 유전자)은 정말로 꺼져 있습니다.
이건 정보입니다.

가짜 0 — 놓쳤다 (dropout)

세포 하나에서 mRNA 를 전부 잡지 못합니다.
10x 는 분자의 10~30% 정도만 포착해요.
켜져 있었는데 못 본 겁니다.

이 둘을 구별할 수 없다는 것이 단일세포 자료의 근본적인 한계입니다. 그래서 "이 세포에 Cd8a 가 0이니 CD8 T세포가 아니다" 라고 단정하면 안 됩니다. 대신 세포를 여럿 묶어서(군집) 평균을 보거나, 여러 표지자를 함께 봅니다. 12~14강에서 하게 될 일이 정확히 이것입니다.

세포 하나가 보여 주는 것은 유전자 전체의 5~10% 남짓입니다.
Python 브라우저에서 실행
🤔 확인 문제
CSC 형식에서 indptr 의 길이가 1,201 이라면?
🤔 확인 문제
어떤 세포에서 Cd8a 값이 0 이었습니다. 올바른 해석은?
🤔 확인 문제
이 조각 파일에서 X.todense() 를 부르는 것이 그나마 가능한 이유는?