๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
HYU/์กธ์—… ํ”„๋กœ์ ํŠธ

3. Community Detection

by Jaeguk 2024. 2. 9.

์ง€๋‚œ ํ™œ๋™ ์š”์•ฝ


์ง€๋‚œ ํ™œ๋™์„ ๋ฐ”ํƒ•์œผ๋กœ ์ด๋ฒˆ์—๋Š” ์–ด๋–ค ์ž‘์—…์„ ํ• ์ง€ ์ •ํ•ด๋ณด์•˜๋‹ค

์ง€๋‚œ ๋ฒˆ์— Cytoscape์—๋Š” ์–ด๋–ค ๋ ˆ์ด์•„์›ƒ๋“ค์ด ์กด์žฌํ•˜๋Š”์ง€ ํŒŒ์•…ํ•˜๊ณ , ๊ฐ ๋ ˆ์ด์•„์›ƒ๋“ค์˜ ์„ฑ๋Šฅ์„ ๊ฐ€๋ณ๊ฒŒ ์ธก์ •ํ•ด๋ณด์•˜๋‹ค.

๊ทธ ๊ฒฐ๊ณผ fcose ๋ ˆ์ด์•„์›ƒ์ด ์šฐ๋ฆฌ์˜ ์ฃผ์ œ์™€ ๊ฐ€์žฅ ์ ํ•ฉํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ณ , ์„ฑ๋Šฅ๋„ ๋‚˜์˜์ง€ ์•Š์•˜๋‹ค.

=> fcose ๋ ˆ์ด์•„์›ƒ์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋‹ค๋ฅธ ๋ ˆ์ด์•„์›ƒ๋“ค์€ ์œ ์ €๊ฐ€ ์„ ํƒ์‹œ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๊ฒฐ์ •์„ ๋‚ด๋ ธ๋‹ค.

์ด๋ฒˆ ํ™œ๋™ ์š”์•ฝ


์ด๋ฒˆ์— ํ•ด์•ผ ํ•  ์ž‘์—…์„ ํฌ๊ฒŒ 2๊ฐ€์ง€๋กœ ์ •ํ–ˆ๋‹ค

1. ๊ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ณ„๋กœ ๋‹ค๋ฅธ ์ƒ‰ ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๊ณ ,

2. Degree๊ฐ€ ํฐ ๋…ธ๋“œ์ผ์ˆ˜๋ก ๋…ธ๋“œ์˜ ํฌ๊ธฐ๋ฅผ ํฌ๊ฒŒ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

Degree์— ๋”ฐ๋ผ ํฌ๊ธฐ ์กฐ์ ˆํ•˜๊ธฐ


Degree๊ฐ€ ํฐ ๋…ธ๋“œ๋Š” ํฌ๊ธฐ๋ฅผ ํฌ๊ฒŒ ํ‘œํ˜„ํ•ด์„œ ์ธ์‹ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ

๋น„๊ต์  ์‰ฌ์›Œ๋ณด์ด๋Š” ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•˜๋Š” ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๋„์ž…ํ–ˆ๋‹ค.

์‰ฌ์›Œ๋ณด์˜€๋˜ ์ด์œ ๋Š” Cytoscape.js ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ ์ค‘์— degree๋ฅผ ์ธก์ •ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

export const styleSheet: StyleType[] = [
  {
    selector: "node",
    style: {
      backgroundColor: (ele): string => ele._private.data.color,
      width: (ele) => {
        const degree = ele.degree();
        const scalingFactor = degree >= 30 ? 10 : 20; // ์Šค์ผ€์ผ๋ง ํŒฉํ„ฐ ์กฐ์ •
        const minSize = 20; // ์ตœ์†Œ ํฌ๊ธฐ
        const maxSize = 80; // ์ตœ๋Œ€ ํฌ๊ธฐ

        // Degree์— ๋น„๋ก€ํ•˜์—ฌ ํฌ๊ธฐ๋ฅผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐ
        const size = degree * scalingFactor;

        // ์ตœ์†Œ ๋ฐ ์ตœ๋Œ€ ํฌ๊ธฐ ์ ์šฉ
        return Math.max(minSize, Math.min(size, maxSize)) as number;
      },
      height: (ele) => {
        const degree = ele.degree();
        const scalingFactor = degree >= 30 ? 10 : 20;
        const minSize = 20;
        const maxSize = 80;

        const size = degree * scalingFactor;

        return Math.max(minSize, Math.min(size, maxSize));
      },
      // Adjust the scaling factor as needed

      label: "data(label)",
      ...
     }

๊ทธ๋ž˜ํ”„์˜ ์Šคํƒ€์ผ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” styleSheet์—์„œ width, height ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์ด์ „์—๋Š” width, height๋ฅผ ์ƒ์ˆ˜๊ฐ’์œผ๋กœ ๊ณ ์ •ํ–ˆ์—ˆ์ง€๋งŒ, ์ด์ œ๋Š” degree๊ฐ’์„ ์ด์šฉํ•ด์„œ ๋™์ ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

๋‹จ์ˆœํžˆ degree์— ๋น„๋ก€ํ•ด์„œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ปค์ง€๋„๋ก ํ•˜๋ฉด degree๊ฐ€ ์••๋„์ ์œผ๋กœ ํฐ ๋…ธ๋“œ๊ฐ€ ์กด์žฌํ•˜๋ฉด ๋‚˜๋จธ์ง€ ๋…ธ๋“œ๋“ค์ด ๋„ˆ๋ฌด ์ž‘์•„์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ตœ๋Œ€ ์ตœ์†Œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ–ˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ ์ด๋ ‡๊ฒŒ degree์— ๋”ฐ๋ผ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•˜๋Š” ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์ปค๋ฎค๋‹ˆํ‹ฐ๋ณ„๋กœ ๋…ธ๋“œ ์ƒ‰์ƒ ๋‹ค๋ฅด๊ฒŒ ํ•˜๊ธฐ


์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ Detectionํ•ด์„œ ๊ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ๋งˆ๋‹ค ๋‹ค๋ฅธ ์ƒ‰์ƒ์„ ๋ถ€์—ฌํ•œ๋‹ค

๋ฌธ์ œ๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ detectionํ•ด์„œ ๋…ธ๋“œ์˜ ์ƒ‰์ƒ์„ ๋‹ค๋ฅด๊ฒŒ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

์‚ฌ์ „์— ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ทธ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ƒ‰์ƒ์„ ์ง€์ •ํ•˜๋ฉด ๋˜๊ฒ ์ง€๋งŒ, ๋“ค์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ์—๋Š” Node์™€ Edge ์ •๋ณด๋งŒ ์กด์žฌํ•  ๋ฟ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋Œ€ํ•œ ์ •๋ณด๋Š” ์—†์—ˆ๋‹ค.

๋จผ์ € Cytosacpe์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ํƒ์ง€ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”์ง€๋ฅผ ์‚ดํŽด๋ณด์•˜์ง€๋งŒ, ๋‚ด๋ถ€์ ์œผ๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ํƒ์ง€ํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ๊ทธ๊ฑธ ์ด์šฉํ•ด์„œ ๋‚ด๊ฐ€ ์ƒ‰์ƒ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค.

์ฑ—์ง€ํ”ผํ‹ฐ์—๊ฒŒ๋„ ๋ฌผ์–ด๋ดค์ง€๋งŒ, ๋ฏธ๋ฆฌ ์•Œ๊ณ ์žˆ๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ์ •๋ณด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ƒ‰์ƒ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•๋งŒ์„ ์•Œ๋ ค์ค„ ๋ฟ, ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ๋จผ์ € detectionํ•˜๊ณ  ๊ทธ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ƒ‰์ƒ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•Œ๋ ค์ฃผ์ง€ ์•Š์•˜๋‹ค.

https://stackoverflow.com/questions/54821322/how-to-set-different-colors-for-each-group-with-cytoscape-js

 

How to set different colors for each group with cytoscape.js

I'm learning cytoscape.js recently, and know that set backgroud color for group with the following code : { selector: ':parent', style: { 'background-color': '#ededeb', '

stackoverflow.com

์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ์—์„œ ๋‚˜์™€ ๊ฐ™์€ ์ž‘์—…์„ ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์‚ฌ๋žŒ์„ ์ฐพ์•˜์ง€๋งŒ, ์ด ์‚ฌ๋žŒ์€ ๋ถ€๋ชจ ๋…ธ๋“œ์™€ ๊ฐ™์€ ์ƒ‰์ƒ์„ ์น ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ–ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์šฐ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ๋Š” ์–‘๋ฐฉํ–ฅ ๊ฐ„์„ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€๋ชจ ๋…ธ๋“œ๋ผ๋Š” ๊ฐœ๋…์ด ์กด์žฌํ•˜์ง€ ์•Š์•˜๋‹ค.
๊ทธ๋ ‡์ง€๋งŒ ์ด ๊ธ€์„ ๋ณด๊ณ  ์˜๊ฐ์„ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๊ฐ ๋…ธ๋“œ๊ฐ€ ๊ฐ™์€ ์ปค๋ฎคํ‹ฐ๋‹ˆ์— ์†ํ•ด์žˆ๋Š”์ง€ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์„๊นŒ?

๊ณ ๋ฏผ๋์— ์œ ๋‹ˆ์˜จ ํŒŒ์ธ๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ด ๋– ์˜ฌ๋ž๋‹ค.

๊ฐ™์€ ์ง‘ํ•ฉ์— ์†Œ์†๋œ ๋…ธ๋“œ๋ผ๋ฉด ๋‹น์—ฐํ•˜๊ฒŒ ๊ฐ™์€ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์†ํ•œ ๋…ธ๋“œ์ผ ๊ฒƒ์ด๋‹ค.

//๋…ธ๋“œ ์ƒ‰์ƒ ๋ชฉ๋ก
{
  ...
  const colors: string[] = [
    "#ff6961",
    "#77dd77",
    "#e79eff",
    "#84b6f4",
    "#b0f2c2",
    "#ffda9e",
  ];

  let colorIdx = 0;

  const parent: { [key: string]: Node } = {};

  const Find = (nowNode: Node): Node => {
    const parentNode = parent[nowNode.node];
    if (nowNode.node === parentNode.node) return parentNode;
    else return (parent[nowNode.node] = Find(parentNode));
  };

  const Union = (nodeA: Node, nodeB: Node): void => {
    const parentA = parent[nodeA.node];
    const parentB = parent[nodeB.node];

    parent[parentA.node] = parentB;
  };

  lines.forEach((line) => {
    const parts = line.split("\t");

    const sourceId = `${parts[0]}`;
    const targetId = `${parts[1]}`;

    const sourceNode = {
      node: sourceId,
      color:
        parent[sourceId] === undefined
          ? colors[colorIdx++ % colors.length]
          : parent[sourceId].color,
    };

    const targetNode = {
      node: targetId,
      color:
        parent[targetId] === undefined
          ? colors[colorIdx++ % colors.length]
          : parent[targetId].color,
    };

    //๋งŒ์•ฝ ๋ถ€๋ชจ ๋…ธ๋“œ๊ฐ€ ์—†๋‹ค๋ฉด ์ž๊ธฐ ์ž์‹ ์œผ๋กœ ์„ธํŒ…
    if (parent[sourceNode.node] === undefined) {
      parent[sourceId] = sourceNode;
    }
    if (parent[targetNode.node] === undefined) {
      parent[targetId] = targetNode;
    }

    //๊ฐ™์€ ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด Union
    if (Find(sourceNode).node !== Find(targetNode).node) {
      Union(sourceNode, targetNode);
    }
    ...
  }

  data.nodes.map((nowNode) => {
    const coloredNode = {
      ...nowNode.data,
      color: Find({ node: nowNode.data.id as string, color: "" }).color,
    };
    nowNode.data = coloredNode;
  });

๋ฌผ๋ก  ์–‘๋ฐฉํ–ฅ ๊ฐ„์„ ์ด์ง€๋งŒ, ํ•œ์ชฝ์ด ๋ถ€๋ชจ๊ฐ€ ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์ง„ํ–‰ํ–ˆ๋‹ค.

=> ์–ด์งœํ”ผ ๊ฐ™์€ ์ง‘ํ•ฉ์— ์†ํ•˜๋Š”์ง€๋งŒ ์•Œ์•„๋‚ด๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ

 

๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ ์ตœ์ข…์ ์œผ๋กœ ์ตœ์ƒ์œ„ ๋ถ€๋ชจ๋…ธ๋“œ์˜ ์ƒ‰์ƒ์„ ๋”ฐ๋ฅด๋„๋ก ์ƒ‰์ƒ์„ ์„ค์ •ํ–ˆ๋‹ค.

๋…ธ๋“œ์˜ ์ˆ˜๊ฐ€ ๋งŽ์•„๋„ ์ˆ˜์ฒœ๊ฐœ์ผ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ–ˆ๊ณ ,
๊ฒฝ๋กœ ์••์ถ•์„ ํ–ˆ์„ ๋•Œ Find์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” ์ƒ์ˆ˜์‹œ๊ฐ„์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ Œ๋”๋ง ์†๋„์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ–ˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ ์ด๋ ‡๊ฒŒ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ƒ‰์ƒ์„ ๋ ๋„๋ก ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
์กฐ๊ธˆ ๋” ๊ตฌ๋ถ„์ด ์ž˜๋˜๋Š” ์ƒ‰์ƒ๋“ค๋กœ ์ง€์ •ํ•˜๋ฉด ๋” ์„ ๋ช…ํ•˜๊ฒŒ ํด๋Ÿฌ์Šคํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™๋‹ค.

 

์œ ๋‹ˆ์˜จ ํŒŒ์ธ๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด๋Ÿฐ ๊ณณ์—์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ์ค„์€ ๋ชฐ๋ž๋‹ค.

์ด๋Ÿฐ ๋ฐฉ๋ฒ•์ด ๋– ์˜ฌ๋ž๋‹ค๋Š” ๊ฒƒ์ด ๋ฟŒ๋“ฏํ–ˆ๊ณ , ์‹ค์ œ ์ ์šฉ์ด ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ๋„ ์‹ ๊ธฐํ–ˆ๋‹ค.

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ์™œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ทธ๋ ‡๊ฒŒ ๊ฐ•์กฐํ•˜๊ณ , ์ค‘์š”ํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๋Š”์ง€ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

๋‹ค์Œ ๋ชฉํ‘œ


1. ๊ธฐ์กด์— ์กด์žฌํ•˜๋˜ ์›น ํŽ˜์ด์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ์ž˜ ์—ฎ์–ด์„œ migration ํ•  ๊ฒƒ์ธ์ง€ ๊ณ ๋ฏผ

2. ์–ด๋–ค ์‹์œผ๋กœ ๋””์ž์ธ ์„ ํ•ด์•ผ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ž˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„์ง€ ๊ณ ๋ฏผ

728x90