- PVSM.RU - https://www.pvsm.ru -

Визуализация пересечений и перекрытий с помощью Python

Изучение вариантов решения одной из самых сложных задач визуализации данных

Преобладающая задача в любом анализе данных — сравнение нескольких наборов чего-либо. Это могут быть списки IP-адресов для каждой целевой страницы вашего сайта, клиенты, которые купили определённые товары в вашем магазине, несколько ответов из опроса и многое другое.

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

Визуализация пересечений и перекрытий с помощью Python - 1


Диаграмма Венна


В следующих примерах я воспользуюсь датасетом [1] из переписи общества визуализации данных 2020 года [2].

Я буду работать с опросом, потому что в нём много разнотипных вопросов; некоторые из них — это вопросы со множественным выбором и несколькими ответами, как показано ниже.

Визуализация пересечений и перекрытий с помощью Python - 2

Источник — Datavisualizationsurvey Git [1]

Допустим, мы будем считать каждый ответ. В нашей диаграмме итоговые числа будут больше, чем общее число респондентов, что может вызвать трудности в понимания аудиторией, будут подниматься вопросы, непонимание заставит аудиторию скептически относиться к данным.

Например, если бы у нас было 100 респондентов и три возможных ответа — A, B и C.

У нас может быть что-то вроде этого:
50 ответов — A и B;
25 ответов — А и С;
25 ответов — А.

Визуализация пересечений и перекрытий с помощью Python - 3

Гистограмма

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

Кроме того, с такой визуализацией у нас нет никакой информации о пересечении ответов. Например, нельзя сказать, что никто не выбрал все три варианта.

Диаграммы Венна


Давайте начнём с простого и очень знакомого решения — диаграмм Венна. Я использую Matplotlib-Venn [3] для этой задачи.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib_venn import venn3, venn3_circles
from matplotlib_venn import venn2, venn2_circles

Теперь загрузим набор данных и подготовим данные, которые хотим проанализировать.

Вопрос, который мы проверим: «Что из этого лучше всего описывает вашу роль в качестве визуализатора данных за прошедший год?»

Ответы на этот вопрос распределены по 6 столбцам, по одному на каждый ответ. Если респондент выбрал ответ, в поле появится текст. Если нет, поле будет пустым. Мы преобразуем эти данные в 6 списков, содержащих индексы выбравших каждый ответ пользователей.

df = pd.read_csv('data/2020/DataVizCensus2020-AnonymizedResponses.csv')
nm = 'Which of these best describes your role as a data visualizer in the past year?'
d1 = df[~df[nm].isnull()].index.tolist() # independent
d2 = df[~df[nm+'_1'].isnull()].index.tolist() # organization
d3 = df[~df[nm+'_2'].isnull()].index.tolist() # hobby
d4 = df[~df[nm+'_3'].isnull()].index.tolist() # student
d5 = df[~df[nm+'_4'].isnull()].index.tolist() # teacher
d6 = df[~df[nm+'_5'].isnull()].index.tolist() # passive income

Диаграммы Венна просты в понимании и применении.

Нам нужно передать наборы с ключами/предложениями, которые мы будем анализировать. Если это пересечение двух наборов, воспользуемся Venn2; если это три набора, тогда используем Venn3.

venn2([set(d1), set(d2)])
plt.show()

Визуализация пересечений и перекрытий с помощью Python - 4

Диаграмма Венна

Здорово! С помощью диаграмм Венна мы можем чётко показать, что 201 респондент выбрал А и не выбрал B, 974 респондента выбрали B и не выбрали A, а 157 респондентов выбрали A и B.

Можно даже настроить некоторые аспекты графика.

venn2([set(d1), set(d2)], 
      set_colors=('#3E64AF', '#3EAF5D'), 
      set_labels = ('FreelancenConsultantn
Independent contractor', 
                    'Position in an organizationnwith some data
viz job responsibilities'),
      alpha=0.75)
venn2_circles([set(d1), set(d2)], lw=0.7)
plt.show()

Визуализация пересечений и перекрытий с помощью Python - 5
venn3([set(d1), set(d2), set(d5)],
      set_colors=('#3E64AF', '#3EAF5D', '#D74E3B'), 
      set_labels = ('FreelancenConsultantn
Independent contractor', 
                    'Position in an organizationnwith some data 
viz job responsibilities',
                    'AcademicnTeacher'),
      alpha=0.75)
venn3_circles([set(d1), set(d2), set(d5)], lw=0.7)
 
plt.show()

Визуализация пересечений и перекрытий с помощью Python - 6

Это здорово, но что, если мы захотим отобразить перекрытия более трёх наборов? Здесь есть пара возможностей. Например, мы могли бы использовать несколько диаграмм.

labels = ['FreelancenConsultantnIndependent contractor',
          'Position in an organizationnwith some data viznjob responsibilities', 
          'Non-compensatedndata visualization hobbyist',
          'Student',
          'Academic/Teacher',
          'Passive income fromndata visualizationnrelated products']
c = ('#3E64AF', '#3EAF5D')
# subplot indexes
txt_indexes = [1, 7, 13, 19, 25]
title_indexes = [2, 9, 16, 23, 30]
plot_indexes = [8, 14, 20, 26, 15, 21, 27, 22, 28, 29]
# combinations of sets
title_sets = [[set(d1), set(d2)], [set(d2), set(d3)], 
              [set(d3), set(d4)], [set(d4), set(d5)], 
              [set(d5), set(d6)]]
plot_sets = [[set(d1), set(d3)], [set(d1), set(d4)], 
             [set(d1), set(d5)], [set(d1), set(d6)],
             [set(d2), set(d4)], [set(d2), set(d5)],
             [set(d2), set(d6)], [set(d3), set(d5)],
             [set(d3), set(d6)], [set(d4), set(d6)]]
fig, ax = plt.subplots(1, figsize=(16,16))
# plot texts
for idx, txt_idx in enumerate(txt_indexes):
    plt.subplot(6, 6, txt_idx)
    plt.text(0.5,0.5,
             labels[idx+1], 
             ha='center', va='center', color='#1F764B')
    plt.axis('off')
# plot top plots (the ones with a title)
for idx, title_idx in enumerate(title_indexes):
    plt.subplot(6, 6, title_idx)
    venn2(title_sets[idx], set_colors=c, set_labels = (' ', ' '))
    plt.title(labels[idx], fontsize=10, color='#1F4576')
# plot the rest of the diagrams
for idx, plot_idx in enumerate(plot_indexes):
    plt.subplot(6, 6, plot_idx)
    venn2(plot_sets[idx], set_colors=c, set_labels = (' ', ' '))
plt.savefig('venn_matrix.png')

Визуализация пересечений и перекрытий с помощью Python - 7

Матрица диаграммы Венна

Ничего страшного, но это не решило проблему. Мы не можем определить, есть ли кто-то, кто выбрал все ответы, а также невозможно определить пересечение трёх наборов. Как насчёт диаграммы Венна с четырьмя кругами?

Визуализация пересечений и перекрытий с помощью Python - 8

Здесь всё начинает усложняться. На изображении выше нет пересечения только синего и зелёного. Чтобы решить эту проблему, вместо кругов мы можем использовать эллипсы.

В двух следующих примерах применяется PyVenn [4].

from venn import venn
sets = {
    labels[0]: set(d1),
    labels[1]: set(d2),
    labels[2]: set(d3),
    labels[3]: set(d4)
}
fig, ax = plt.subplots(1, figsize=(16,12))
venn(sets, ax=ax)
plt.legend(labels[:-2], ncol=6)

Визуализация пересечений и перекрытий с помощью Python - 9

Вот оно!

Но мы потеряли размер — критически важную для диаграммы информацию. Синий (807) меньше жёлтого (62), что не очень помогает в визуализации. Чтобы понять, что есть что, мы можем использовать легенду и метки, но таблица была бы яснее.

Есть несколько реализаций пространственных пропорциональных диаграмм Венна, которые могут работать с более чем тремя наборами, но на Python я не смог найти ни одной.

График UpSet


Но есть и другое решение. Графики UpSet [5] — отличный способ отображения пересечения нескольких множеств. Они не так интуитивно понятны для чтения, как диаграммы Венна, но делают свою работу. Я воспользуюсь UpSetPlot [6], но сначала подготовлю данные.

upset_df = pd.DataFrame()
col_names = ['Independent', 'Work for Org', 'Hobby', 'Student', 'Academic', 'Passive Income']
nm = 'Which of these best describes your role as a data visualizer in the past year?'
for idx, col in enumerate(df[[nm, nm+'_1', nm+'_2', nm+'_3', nm+'_4', nm+'_5']]):
    temp = []
    for i in df[col]:
        if str(i) != 'nan':
            temp.append(True)
        else:
            temp.append(False)
    upset_df[col_names[idx]] = temp
    
upset_df['c'] = 1
example = upset_df.groupby(col_names).count().sort_values('c')
example

Визуализация пересечений и перекрытий с помощью Python - 10

При правильном расположении данных нам нужен только один метод, чтобы нарисовать нашу диаграмму, и всё.

upsetplot.plot(example['c'], sort_by="cardinality")
plt.title('Which of these best describes your role as a data visualizer in the past year?', loc='left')
plt.show()

Визуализация пересечений и перекрытий с помощью Python - 11

График UpSet

Потрясающе! Наверху — столбцы, показывающие, сколько раз появлялась комбинация. Внизу — матрица, показывающая, какую комбинацию представляет каждый столбец, а внизу слева — горизонтальная гистограмма, представляющая общий размер каждого набора.

Это большое количество информации, но хорошо организованный макет позволяет легко извлекать её.

Даже с моими плохо написанными метками мы легко можем увидеть, что большинство людей выбрали «работать на организацию».

Второй наиболее распространённый ответ даже не отображался на предыдущих диаграммах Венна: количество людей, которые не выбрали ни одного ответа.

В целом визуализация множеств и их пересечений может быть задачей для решения в уме, но у нас есть несколько хороших вариантов её решения.

Я предпочитаю диаграммы Венна, когда имею дело с небольшим количеством множеств, и графики Upset, когда множеств больше трёх. Всегда полезно объяснить, что показывает визуализация и как читать диаграммы, которые вы представляете, особенно в случаях, когда диаграммы не очень дружелюбны.

Визуализация пересечений и перекрытий с помощью Python - 12

Визуализация пересечений и перекрытий с помощью Python - 13

Визуализация трех наборов

Визуализация пересечений и перекрытий с помощью Python - 14

Визуализация пересечений и перекрытий с помощью Python - 15

Визуализация шести наборов

image

Автор: one-two

Источник [24]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/blog-kompanii-skillfactory/360420

Ссылки в тексте:

[1] датасетом: https://github.com/data-visualization-society/data_visualization_survey/tree/master/data/2020

[2] переписи общества визуализации данных 2020 года: https://www.datavisualizationsociety.com/census

[3] Matplotlib-Venn: https://pypi.org/project/matplotlib-venn/

[4] PyVenn: https://pypi.org/project/venn/

[5] Графики UpSet: https://ieeexplore.ieee.org/document/6876017

[6] UpSetPlot: https://pypi.org/project/UpSetPlot/

[7] Обучение профессии Data Science: https://skillfactory.ru/dstpro?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_DSPR&utm_term=regular&utm_content=060121

[8] Обучение профессии Data Analyst: https://skillfactory.ru/dataanalystpro?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_DAPR&utm_term=regular&utm_content=060121

[9] Профессия Java-разработчик: https://skillfactory.ru/java?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_JAVA&utm_term=regular&utm_content=060121

[10] Профессия Frontend-разработчик: https://skillfactory.ru/frontend?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_FR&utm_term=regular&utm_content=060121

[11] Профессия Веб-разработчик: https://skillfactory.ru/webdev?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_WEBDEV&utm_term=regular&utm_content=060121

[12] Профессия Этичный хакер: https://skillfactory.ru/cybersecurity?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_HACKER&utm_term=regular&utm_content=060121

[13] Профессия C++ разработчик: https://skillfactory.ru/cplus?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_CPLUS&utm_term=regular&utm_content=060121

[14] Профессия Разработчик игр на Unity: https://skillfactory.ru/game-dev?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_GAMEDEV&utm_term=regular&utm_content=060121

[15] Профессия iOS-разработчик с нуля: https://skillfactory.ru/iosdev?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_IOSDEV&utm_term=regular&utm_content=060121

[16] Профессия Android-разработчик с нуля: https://skillfactory.ru/android?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_ANDR&utm_term=regular&utm_content=060121

[17] Курс по Machine Learning: https://skillfactory.ru/ml-programma-machine-learning-online?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_ML&utm_term=regular&utm_content=060121

[18] Продвинутый курс «Machine Learning Pro + Deep Learning»: https://skillfactory.ru/ml-and-dl?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_MLDL&utm_term=regular&utm_content=060121

[19] Курс «Python для веб-разработки»: https://skillfactory.ru/python-for-web-developers?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_PWS&utm_term=regular&utm_content=060121

[20] Курс по JavaScript: https://skillfactory.ru/javascript?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_FJS&utm_term=regular&utm_content=060121

[21] Курс «Математика и Machine Learning для Data Science»: https://skillfactory.ru/math_and_ml?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_MATML&utm_term=regular&utm_content=060121

[22] Курс по аналитике данных: https://skillfactory.ru/analytics?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_SDA&utm_term=regular&utm_content=060121

[23] Курс по DevOps: https://skillfactory.ru/devops?utm_source=infopartners&utm_medium=habr&utm_campaign=habr_DEVOPS&utm_term=regular&utm_content=060121

[24] Источник: https://habr.com/ru/post/536228/?utm_source=habrahabr&utm_medium=rss&utm_campaign=536228