Визуализация каталогов на Python средствами NetworkX

Листая на Хабре раздел Python наткнулся на интересную статью о библиотеке NetworkX. Впечатлившись красивыми графами, решил повысить свой python-скилл и покопаться в networkx.
image

Пролог


Первый вопрос — откуда взять данные для визуализации? Генерировать случайные не интересно, они и в комплекте модуля были. Тут вспомнилась Dos утилитка tree, выводящая каталоги файловой системы в виде дерева. Решено было написать красивый аналог на Python и нарисовать все в networkx с помощью matplotlib.

Акт первый


Copy Source | Copy HTML
  1. def get_tree(tree=[u"E:\\Музыка",], G=nx.Graph(), itr= 0, max_itr=900):
  2.     point = tree.pop( 0)
  3.     itr = itr + 1
  4.     sub_tree = [os.path.join(point, x) for x in os.listdir(point) if os.path.isdir(os.path.join(point, x)) and not is_hidden_dir(os.path.join(point, x))]
  5.     if sub_tree:
  6.         tree.extend(sub_tree)
  7.         G.add_edges_from(map(lambda b : (point, b), sub_tree))
  8.     if tree and itr <= max_itr:
  9.         return get_tree(tree, G, itr)
  10.     else:
  11.         return G

Тут уже становится понятно, что музыки не так уж и много, судя по получившемуся графу.
Сам по себе код не представляет ничего сложно. Функция рекурсивно вызывает сама себя пока не закончится список директорий на разбор tree или пока не выйдет установленное количество итераций. При каждом вызове из списка директорий «выталкивается» первый элемент, у которого ищутся подкаталоги стандартной python функцией os.listdir(). После чего списки сливаются и полученные связи добавляются в структуру графа. Главной хитростью является функция is_hidden_dir(). Она проверяет, является ли файл скрытым. Вначале я думал, что эту тривиальную задачу можно решить средствами самого языка. Но оказалось, что это прост только в Unix системах, а в Windows, под которой я пишу, эта задача приобретает оттенки мазохизма.

Акт второй


Copy Source | Copy HTML
  1. def is_hidden_dir(d):
  2.     import sys, subprocess
  3.     if sys.platform.startswith("win"):
  4.         p = subprocess.check_output(["attrib", d])
  5.         return True if 'H' in p[:12] else False
  6.     else:
  7.         return True if os.path.basename(d)[ 0] == '.' else False


Сначала проверяем систему, пользователя. Если не Windows, то определение свойства hidden у файла банально — первым символом каталога должна быть точка «.».
В случае винды все нетипичнее. Подумав как можно сделать нечто подобное, я понял, что явных свойств у файла как точка в nix'ах не будет. Разбираться с WinAPI под Python не было желания. Оставалось воспользоваться грязными хаками, что я и сделал.

Быстрым поиском в гугле удалось найти консольную утилитку attrib. Изначально планировалось просто запустить ее через os.system(), но документация меня отговорила. Тем более мне был важен вывод утилиты, а не сам факт ее успешной работы. В манах нашлась нужная функция subprocess.check_output(), возвращающая результат работы аргумента. Дальнейшая проверка проста, в первых 12 символах надо найти вхождение флага «H». Но и тут виндовс не дал мне расслабиться. Пока он обрабатывал директории, в именах которых присутствовали только латинские символы, все было спокойно, но когда он добрался до русских букв, то работать отказался наотрез. Логично, что его не устраивала кодировка. Я с легким сердцем вставил декодер
Copy Source | Copy HTML
  1. p = subprocess.check_output(["attrib", d.encode('cp1251')])

Но питон быстро переубедил меня в успешности такой идее, отказавшись на этот раз работать с англоязычными названиями.
Итогом стало компромиссное решение.
Copy Source | Copy HTML
  1. p = subprocess.check_output(["attrib", d.encode('cp1251') if isinstance(d, unicode) else d])


Акт третий. Заключительный


Осталось лишь нарисовать сгенерированный граф на networkx, ради чего все и затевалось. Не надо долго гадать, что бы понять, что и тут надо было поковыряться. Функция отрисовки заманчиво проста:
Copy Source | Copy HTML
  1. import networkx as nx
  2. import matplotlib.pyplot as plt
  3.  
  4. def main():
  5.     G = get_tree()
  6.     nx.draw(G, with_labels=False, node_color="blue", alpha= 0.6, node_size=50)
  7.     plt.savefig("edge_colormap.png")
  8.     plt.show()

Параметры указательные в nx.draw() не обязательны, кроме самого графа. NetwokX поддерживает 2 библиотеки отрисовки визуальных данных: matplotlib и pygraphviz. Сначала я решил использовать pygraphviz. Скачал его с оффсайта, установил, начал устанавливать для него обертку под питон pygraphviz, но тут pip ругнулся и сказал, что pygraphviz отказывается иметь дело с windows. Ладно, подумал я, у нас есть альтернатива. Matplotlib встал без лишних вопросов, но при запуске скрипта с графом начал возмущаться, что я до сих пор не пользуюсь NumPy. Скачали и поставил NumPy. Он ничего у меня не попросил и просто начал работать.

Итоговый код выглядит так:
Copy Source | Copy HTML
  1. #-*- encoding: utf-8
  2.  
  3. import networkx as nx
  4. import matplotlib.pyplot as plt
  5. import os
  6.  
  7. def get_tree(tree=[u"E:\\Музыка",], G=nx.Graph(), itr= 0, max_itr=900):
  8.     point = tree.pop( 0)
  9.     itr = itr + 1
  10.     sub_tree = [os.path.join(point, x) for x in os.listdir(point) if os.path.isdir(os.path.join(point, x)) and not is_hidden_dir(os.path.join(point, x))]
  11.     if sub_tree:
  12.         tree.extend(sub_tree)
  13.         G.add_edges_from(map(lambda b : (point, b), sub_tree))
  14.     if tree and itr <= max_itr:
  15.         return get_tree(tree, G, itr)
  16.     else:
  17.         return G
  18.  
  19. def is_hidden_dir(d):
  20.     import sys, subprocess
  21.     if sys.platform.startswith("win"):
  22.         p = subprocess.check_output(["attrib", d.encode('cp1251') if isinstance(d, unicode) else d])
  23.         return True if 'H' in p[:12] else False
  24.     else:
  25.         return True if os.path.basename(d)[ 0] == '.' else False
  26.  
  27. def main():
  28.     G = get_tree()
  29.     nx.draw(G, with_labels=False, node_color="blue", alpha= 0.6, node_size=50)
  30.     plt.savefig("edge_colormap.png")
  31.     plt.show()
  32.  
  33. if __name__ == "__main__":
  34.     main()


Занавес


Снимок всей системы получить так и не удалось. Памяти не хватает или рук, что вероятнее.
Несколько картинок напоследок:
<img title="" border=«0» alt="" src="
Поделиться публикацией

Похожие публикации

Комментарии 35

    0
    Оригинальный подход, молодец (:
    Если сделать подобное в 3D и с размером шаров, пропорциональным размеру директорий или количеству файлов в них, получится прикольная утилита для анализа засорённости файлопомойки.
      +1
      Спасибо.
      Про соответствие шаров размеру идея тоже приходила в голову, а про 3D нет.
      Надо будет попробовать. Возможно на чем то более мощном, чем питон. Он крут для быстрого создания прототипов, но пока я не смог заставить змия быстро работать.
        +1
        Не соглашусь, здесь не питон будет узким местом.
        Скорость работы будет, в основном, определяться скоростью работы с файловой системой.
        Расход памяти будет большим в любом случае, и, большей частью, будет определяться потреблением networkx — все-таки число узлов огромное.
          0
          Да, проблема памяти существенна. Возможно, надо сбрасывать данные на диск, по мере увеличения графа. Но возникнет вопрос как потом вывести единый результат.
            0
            Скорее всего, без копания в кишках networkx не обойтись.
            +2
            Немного потестил на 10 000 ребер (случайное соединение между 10 000 узлами).

            Много времени уходит на построение самого графа. В коде это строка:
            nx.draw(G, with_labels=False, node_color="blue", alpha= 0.6, node_size=50)

            При этом было съедено около 50Mb памяти и потребовалось около получаса на построение.
            Так что здесь все больше упирается в процессорное время, а не в память.
              0
              Как Вы думаете, стоит ли поменять библиотеку с matplotlib на pygraphviz для ускорения прорисовки? Pygraphviz это прямой интерфейс к библиотеке, а matplotlib делает расчеты, базируясь на NumPy, что может замедлять работу.
            +2
            И питон потянет, Вы просто не умеете его готовить.
            Когда работаете с такими вещами, думаю и на C пришлось бы оптимизировать. Нечего весь disk tree в память структурой пихать :).
            Жаль 3д мне не дается, так бы мог бы помочь.
              0
              И питон потянет, Вы просто не умеете его готовить.

              Подозреваю, что так и есть.
              Нечего весь disk tree в память структурой пихать :).

              Как вариант, думаю, можно разбить прохождение диска на несколько этапови попытаться склеить результаты.
            +1
            Лет шесть уже пользуюсь маленькой программкой Scanner для анализа той самой помойки. Там все реализовано не графом, а разветвленной диаграммой.
            Но интересно то, что там же лежат Delphi sources утилиты. Работает быстро, думаю интересно будет покопаться.
              0
              Очень напоминает стандартный убунтовский «Анализатор использования дисков». Но последний кроме прочего умеет работать по сети (FTP, SSH, SMB и др.)
                0
                классно, но вот шарики в 3D бы реализовать, так вообще просто круто было бы :)
                  0
                  Возможно, он не по каталогам ползет, а сразу читает Master File Table? Хотя, надо взглянуть на исходники.
                    0
                    w3.win.tue.nl/nl/onderzoek/onderzoek_informatica/visualization/sequoiaview/

                    Прикольный софт, квадратами всё кажет…
                  0
                  Интересное применение networkx.
                  Сейчас сам с ней работаю. Мы ее используем для построения схемы сети — дает очень хорошую картинку.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      +2
                      Строится автоматически. Руками можно перемещать узлы после построения. До релиза поделиться примерами и подробностями не могу, только картинкой.

                    0
                    Для анализа использования диска есть крутая тулза baobab (Disk Usage Analyzer). Во многих дистрах есть по дефолту
                      +2
                      Под Windows есть весьма крутая утилитка WinDirStat. Выглядит оно как-то так:

                      WinDirStat
                        0
                        ИМХО, им определенно надо радиальный градиет заменить на линейный.
                      0
                      для винды можно решить проще с помощью pywin32 — раз и два.
                        0
                        Да, я видел это расширение. Но было желание не использовать ничего стороннего, кроме networkx.
                        Так что я выбрал путь настоящего ниндзя.
                        +1
                        Я все задачи визуализации графов делаю с помощью graphviz. На питоне скрипты пишут текстовые файлы типа
                        A -> B
                        B -> C
                        B -> D

                        и кормят их приложению graphviz. Полный контроль над внешним видом графов, надписи в узлах и на ребрах и генерация тегов AREA MAP и много еще чего. Правда операций линейной алгебры и теории графов не поддерживает. Не нужно держать в памяти весь граф дисковой системы — по мере обхода дерева просто пишешь в файл. А graphviz очень хорошо справляется с большими графами, правда может сгенерить простыню на 100Мб (графический файл PNG или JPEG).

                        Спасибо за наводку — попробую networkx тоже.
                          0
                          Зачем 'кормить' их приложению graphviz, можно напрямую

                          import gv
                          g = gv.graph('tree')
                          a = gv.node(g, 'A')
                          b = gv.node(g, 'B')
                          e = gv.edge(a, b)

                          gv.layout(g, 'dot')
                          gv.render(g, file_format, filename)
                            0
                            Через gv вы создаете структуру в памяти. Если говорить об очень больших графах, то сливать данные в текстовый файл можно до бесконечности (правда бумаги много понадобится для распечатки графа :)
                            0
                            PS. И лучше в формате PDF или SVG, тогда не будет 'на 100Мб (графический файл PNG или JPEG)'.
                            0
                            Использую NetworkX для проекта. Очень хорош в паре с InfoViz (JS библиотека для визуализации графов) thejit.org/
                            • НЛО прилетело и опубликовало эту надпись здесь
                              +5
                              image
                              D:/Работа/Документы/Старое/Всякий хлам/Нужно удалить/zzzzz/asfsdsfs/Порно? :)
                                0
                                Нее, труъ-ниндзя хранят порно в шифрованных многотомных архивах, разбросанных по разным дискам и замаскированных под DLL, EXE, SYS и прочие файлы, уже своим названием кричащие о своей неприкосновенности :)
                                  +3
                                  Труъ-синоби хранят порно только в черепной коробке.
                                  0
                                  Как найти давно спрятанные и забытые кучи добротного порно при помощи визуализцации списка каталогов, как граф.
                                  0
                                  После прочтения сразу потянуло взять Python + NetworkX и тоже чего нибудь сбацать.
                                    0
                                    гы, аналогично, уже полез тоже ;)
                                    0
                                    ИМХО, с помощью Python нужно собирать/парсить данные и генерировать графы в одном из общепринятых форматов, а анализировать результаты удобнее в Gephi

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое