Как построить дерево с категориями и подкатегориями в Python - PullRequest
2 голосов
/ 14 апреля 2020

Я пытаюсь найти правильный подход к графическому набору данных, который содержит информацию о количестве времени, которое пользователи обычно проводят в разных местах. Важно отметить, что есть категории и подкатегории с возрастающим уровнем детализации моих данных (например, 60% людей находятся в «доме», а 40% из них - в «гостиной»). Я знаю о TreeMaps, которые будут отображать информацию и взаимосвязи, которые мне нужны, но меня попросили сделать «сетевую» визуализацию данных.

Что мне особенно нужно, так это графический подход в Python, который позволил бы мне визуализировать мои данные с помощью узлов (еще лучше, меток узлов), автоматически измеряемых в соответствии с количеством пользователей, попадающих в его категория. Важно, что все дочерние узлы также будут учитываться в родительских узлах (поэтому дендрограммы на самом деле не вариант, потому что мне нужно отображать информацию в каждой точке ветвления).

Мои данные выглядят примерно так (обратите внимание, что некоторые местоположения становятся более гранулированными, чем другие):

| ID | BUILDING | subcat01  | subcat02 |
----------------------------------------
| 00 |  home    | kitchen   | fridge   |
| 01 |  office  | desk      | NaN      |
| 02 |  office  | reception | NaN      |
| 03 |  home    | bedroom   | bed      |
| 04 |  home    | yard      | NaN      |
| 05 |  home    | livingroom| couch    |
| 06 |  office  | conf_room | NaN      |
| 07 | outdoors | NaN       | NaN      |
|... | ...      | ...       | ...      |

Для очень грубого приближения того, что я хочу получить, см. Изображение ниже. Важно то, что я могу измерять узлы в соответствии с суммой их дочерних элементов (или только их самих, если это конечный узел). Я буду выполнять много итераций с различными фильтрами, поэтому мне нужно что-то, что я мог бы легко повторить, а не просто вручную кодировать внешний вид каждого графика.

Есть предложения, какие библиотеки Python могли бы лучше всего выполнить sh? Я кратко изучил networkX , graph-tool и etetoolkit , но я не уверен, что какая-либо из них обладает именно той функциональностью, которую я ищу.

Вот примерное приближение того, что я хочу произвести:

enter image description here

1 Ответ

1 голос
/ 14 апреля 2020

Чтобы сгенерировать график, вы можете установить строки как пути ориентированного графа. Простым способом может быть определение pandas фрейма данных и стек для удаления пропущенных значений:

import networkx as nx
from networkx.drawing.nx_agraph import graphviz_layout
from pylab import rcParams
import pandas as pd
#df = pd.read_csv....
paths = df.loc[:,'BUILDING':].stack().groupby(level=0).agg(list).values.tolist()  
# [['home', 'kitchen', 'fridge'], ['office', 'desk'], ['office', 'reception'],...

Обратите внимание, что стек полезен здесь, поскольку он игнорирует NaN, тогда мы можем просто gorupby в индексе и агрегировать в виде списков. Затем создайте направленный граф и установите пути с помощью nx.add_path:

G = nx.DiGraph()
for path in paths:
    nx.add_path(G, path)

Теперь для визуализации графа в виде в виде дерева макет, мы могли бы использовать graphviz_layout, который в основном является оберткой для pygraphviz_layout:

rcParams['figure.figsize'] = 14, 10
pos=graphviz_layout(G, prog='dot')
nx.draw(G, pos=pos,
        node_color='lightgreen', 
        node_size=1500,
        with_labels=True, 
        arrows=True)

enter image description here

Если вы хотите добавить общий узел source для всех зданий, вы можете вставить столбец с именем ALL сразу после ID:

df.insert(1, 'ALL', 'ALL')
paths = df.loc[:,'ALL':].stack().groupby(level=0).agg(list).values.tolist()  

И затем сделать то же, что и выше, где вы сейчас получите:

enter image description here

Обратите внимание, что есть несколько других программ верстки graphviz, которые могут напоминать больше того, что вы имеете в виду. Например, circo:

pos=graphviz_layout(G, prog='circo')
nx.draw(G, pos=pos,
        node_color='lightgreen', 
        node_size=1500,
        with_labels=True, 
        arrows=True)

enter image description here

...