Прохожу на Курсере курс Applied Social Network Analysis in Python Мичиганского университета. Вообще рекомендую курсы этого университета: много интересного контента. Курсы на Python проходят.
Где первая колонка - отправитель, вторая - получатель, и время отправления. При этом перечень полей не ограничен: это такой датасет в задании, а вообще первое, что приходит на ум - можно добавить роль отправителя и получателя.
И первое, чему я самостоятельно научился по этой теме - раскрашивать узлы (nodes) цветом в зависимости от роли. Как вариант - можно брать не роль, а класс письма: заявка, обращение, нецензурная брань и т.п..
Эта картинка из другого задания.
Если вы хотите потренироваться, запишитесь на курс и скачайте данные второй недели. Или лучше работать со своими данными: можете мне прислать, я буду очень рад поработать с ними.
Код
Прошел я пока две недели, хвастать особо нечем, хочу в посте скорее закрепить свои знания. Заранее предупреждаю, что я не ас, могу писать ерунду в каких-то местах, просто тема для меня новая. А новая потому, что я не могу найти понимания того, куда в HR ее применить. Буду вам благодарен за идеи. Вообще же на Западе тема Organisational Network Analytics очень популярна и набирает темп. Поэтому я и решил подробней в ней разобраться.
Данные е майл коммуникаций
У нас есть данные типа
#Sender Recipient time 1 2 1262454010 1 3 1262454010 1 4 1262454010 1 5 1262454010 1 6 1262454010 1 7 1262454010 1 8 1262454010 1 9 1262454010 1 10 1262454010 1 11 1262454010
И первое, чему я самостоятельно научился по этой теме - раскрашивать узлы (nodes) цветом в зависимости от роли. Как вариант - можно брать не роль, а класс письма: заявка, обращение, нецензурная брань и т.п..
Эта картинка из другого задания.
Если вы хотите потренироваться, запишитесь на курс и скачайте данные второй недели. Или лучше работать со своими данными: можете мне прислать, я буду очень рад поработать с ними.
Код
import networkx as nx import pandas as pd %matplotlib notebook import matplotlib.pyplot as pltЗагрузка
G = nx.read_edgelist('email_network.txt', data=[ ('time', float)], create_using=nx.MultiDiGraph())Я не уверен, что стоит подробно функции объяснять, все равно придется копаться в пакете, отмечу только, что самое важное здесь - MultiDiGraph - мы указываем, что отношения между узлами (nodes) директивные (Di), т.е. письмо идет в определенном направлении от кого то к кому то, т.е. у нас на диаграмме появляется стрелочка. И отношения Multi - может быть много связей, а точнее писем. И data - мы указываем, что время - это атрибут сети, а не сами связи. После загрузки мы получаем объект edges (ребра? грани?)
G.edges(data=True) [('158', '64', {'time': 1271766218.0}), ('154', '68', {'time': 1271766200.0}), ('42', '54', {'time': 1276505582.0}), ('42', '1', {'time': 1266579464.0}), ('42', '61', {'time': 1273146267.0}), ('42', '151', {'time': 1266319958.0}), ('42', '151', {'time': 1268999261.0}), ....Тут все понятно. Количество строк
len(G.edges(data=True)) 82927И вот здесь сразу одна интересная ситуевина. С помощью команды
G.degree() {'1': 3376, '10': 643, '100': 126, '101': 758, '102': 31, '103': 461, '104': 632, '105': 321, '106': 860, '107': 192, '108': 151, '109': 166, '11': 635, '110': 85, ......
Мы получаем список количества писем каждого чувака. И всего у нас
nx.number_of_nodes(G) 167чуваков, а писем
sum(G.degree().values()) 165854Что в два раза больше, чем строк в датасете (82927). Т.е. это значит, что пакет учитывает не только тех, кто слева, но и справа. И если ты значишься не только в числе посылателей писем, но и получателей, вам это будет зачтено.
Визуализация
plt.figure(figsize=(10,9)) node_color = [G.degree(v) for v in G] pos = nx.fruchterman_reingold_layout(G) nx.draw_networkx(G, pos, node_color=node_color, alpha=0.7, with_labels=True, edge_color='.4', cmap=plt.cm.Blues) plt.axis('off') plt.tight_layout();Цвет узла (node) я определил в зависимости от количества получаемых писем
Не очень внятно, верно?) функция pos = nx.fruchterman_reingold_layout(G) отвечает за представление результатов сети, можете поиграться, картинка будет другая. К тому же можно выделить отдельный диапазон картинки.
Матрица.
Можно менять размер узлов (nodes), убирать грани и т.п. Думаю, понятно: я взял самый центр верхней картинки. И третий чувак у нас кажется самым насыщенным по письмам. Мне кажется, это инженер по технике безопасности или кладовщик.
max_value = max(G.degree().values()) max_key, = [i for i in G.degree().keys() if G.degree()[i] == max_value] print('чувак {}\n{} писем'.format(max_key, max_value)) чувак 3 9053 писемДействительно, этот чувак отправил / получил более 9 000 писем.
Метрики
Дальше я покажу несколько метрик сети. Мы проверяем сеть на сильное и слабое соединение.nx.is_strongly_connected(G) nx.is_weakly_connected(G)Строгое соединение такое соединение, при котором любой узел (node) может отправить письмо любому узлу и получить письмо от любого узла, т.е. стрелочки идут во все концы, а слабое - соединение, при котором от каждого узла хотя бы в одну сторону отходит стрелочка.
Машина говорит, что у нас нет строгого соединения, и мы получаем список тех, кто в составе сильного субграфа (где все со всеми) и отщепенцы
[{'133'}, {'102'}, {'126'}, {'125'}, {'136'}, {'132'}, {'114'}, {'130'}, {'127'}, {'135'}, {'131'}, {'116'}, {'166'}, {'1', '10', '100', '101', '103', '104', '105', '106', '107', '108',Обратили внимание, что 102, 126 и т.д. это отщепенцы, а 1, 10, 100 и т.д.... это чуваки не выделяющиеся из массы. С помощью команды
G_sc = max(nx.strongly_connected_component_subgraphs(G), key=len)мы выделяем субграф, где все связаны со всеми, и дальше работаем с ним, как с отдельным графом. И обращаю внимание: этот граф мы уже записываем не как директивный и мульти. В этом графе у нас такие характеристики:
nx.average_shortest_path_length(G_sc) 1.6461587301587302Я дам определение с курса: Average distance between every pair of nodes. Т.е. каждая пара узлов (nodes) может быть связана расстоянием 1 - когда узлы связаны напрямую, 2 - когда между ними кто-то есть еще и т.д.. Так вот average_shortest_path_length показывает среднее расстояние по компании. Фактически тесность связей.
nx.diameter(G_sc) 3Помните про теорию 6 рукопожатий? в данном случае диаметр означает самую длинную связь в компании. Т.е. между самыми дальними чуваками всего три, а не шесть ребер, связей....
nx.eccentricity(G_sc) {'1': 1, '10': 2, '100': 2, '101': 2, '103': 2, '104': 2, '105': 2, '106': 2, '107': 3, ....Т.е. первый чувак со всеми на короткой ноге, а вот 107-й не так чтобы и т.п. А также мы можем вычислить радиус
nx.radius(G_sc) 1Это минимально длинная связь. Т.е. если 3 говорит нам, что самая длинная связь в компании это три, то есть чуваки, которые со всеми связаны через одну связь, т.е. напрямую. И нам осталось вычислить тех, кто имеет самые длинные связи (т.е. это те, кто менее всего включены в коммуникации в компании):
nx.periphery(G_sc) ['134', '129', '97']Вот эти три чувака менее включены. Периферия. А центровые, самые включенные
nx.center(G_sc) ['1', '38']Т.е. по сути это это самые включенные люди. Но сразу предупреждаю - это не робастная метрика. Дальше в курсе обещали показать более надежные характеристики. И в качестве примера. Можно, например, вычислить чувака, у которого больше всего длинных связей (этот будет чувак из списка 134, 129, и 97) и с кем этот чувак в самых длинных связях:
%%time y = [] for i in nx.periphery(G_sc): z = sum(j == nx.diameter(G_sc) for j in nx.shortest_path_length(G_sc,i).values() ) y.append([i,z]) maxex = nx.shortest_path_length(G_sc,max(y)[0]) p = { key:value for key, value in maxex.items() if value == nx.diameter(G_sc) } print('чувак {}\n чуваки {} '.format(max(y)[0], list(p.keys()))) чувак 97 чуваки ['30', '78', '106', '121', '46', '41', '33', '109', '93', '6', '110', '29', '24', '82', '60', '19', '151', '124', '49', '43', '76', '129', '55', '96', '27', '48', '75', '81', '119', '122', '123', '149', '99', '8', '107', '105', '98', '39', '134', '128', '111', '28', '118', '101', '21', '83', '34', '120', '57', '16', '20', '52', '26', '51', '91', '23', '89', '108', '40', '87', '79', '86', '18'] Wall time: 29.1 sИз того, что еще можно вытащить: можно вытащить слабые места графа - те узлы (node), после которых граф распадается как целостный граф.
Ну вот примерно так. Еще раз скажу, что пока не понимаю, что из этого можно выжать для предиктивной HR аналитики. Буду рад вам за советы. Все, что возникает в голове: посмотреть динамику связей новичков после приема - нам это может что-то сказать про адаптация специалиста. Правда, я не представляю себе HR службу, которая бы всерьез этим бы занималась. Или, из прошлого: у нас в банке в конце года филиалы оценивали взаимодействие с центральными службами: IT, бухгалтерией, HR и т.п. - вот эти результаты можно было бы сопроводить подобной перепиской. Можно посмотреть связь показателей эффективности и характером е майл коммуникаций..... ну и т.п..
В любом случае буду рад, если вы мне пришлете свои датасеты, может вместе что-нибудь накопаем.
__________________________________________________________
На этом все, читайте нас в фейсбуке, телеграмме и вконтакте
Комментариев нет:
Отправить комментарий