Сюжет: как определить структуру диаграммы Санки с помощью фрейма данных pandas?

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


Следующий график описывает шотландские выборы и основан на коде из plot.ly < / а>:

Сюжет 1:

введите описание изображения здесь

Набор данных 1:

data = [['Source','Target','Value','Color','Node, Label','Link Color'],
        [0,5,20,'#F27420','Remain+No – 28','rgba(253, 227, 212, 0.5)'],
        [0,6,3,'#4994CE','Leave+No – 16','rgba(242, 116, 32, 1)'],
        [0,7,5,'#FABC13','Remain+Yes – 21','rgba(253, 227, 212, 0.5)'],
        [1,5,14,'#7FC241','Leave+Yes – 14','rgba(219, 233, 246, 0.5)'],
        [1,6,1,'#D3D3D3','Didn’t vote in at least one referendum – 21','rgba(73, 148, 206, 1)'],
        [1,7,1,'#8A5988','46 – No','rgba(219, 233, 246,0.5)'],
        [2,5,3,'#449E9E','39 – Yes','rgba(250, 188, 19, 1)'],
        [2,6,17,'#D3D3D3','14 – Don’t know / would not vote','rgba(250, 188, 19, 0.5)'],
        [2,7,2,'','','rgba(250, 188, 19, 0.5)'],
        [3,5,3,'','','rgba(127, 194, 65, 1)'],
        [3,6,9,'','','rgba(127, 194, 65, 0.5)'],
        [3,7,2,'','','rgba(127, 194, 65, 0.5)'],
        [4,5,5,'','','rgba(211, 211, 211, 0.5)'],
        [4,6,9,'','','rgba(211, 211, 211, 0.5)'],
        [4,7,8,'','','rgba(211, 211, 211, 0.5)']
        ]

Как построен сюжет:

Я собрал некоторые важные детали о поведении графиков Санки из различных источников, например:

Проблема:

Как вы увидите в деталях ниже, узлы, метки и цвета не применяются к диаграмме в том же порядке, что и исходный фрейм данных. Некоторые из этого имеют идеальный смысл, поскольку у вас есть различные элементы, описывающие один и тот же узел, такие как цвет, цели, значения и цвет ссылки. Один узел 'Remain+No – 28' выглядит так:

введите описание изображения здесь

И сопутствующая часть набора данных выглядит так:

[0,5,20,'#F27420','Remain+No – 28','rgba(253, 227, 212, 0.5)'],
[0,6,3,'#4994CE','Leave+No – 16','rgba(242, 116, 32, 1)'],
[0,7,5,'#FABC13','Remain+Yes – 21','rgba(253, 227, 212, 0.5)'],

Итак, эта часть источника описывает узел [0] с тремя соответствующими целями [5, 6, 7] и тремя ссылками со значениями [20, 3, 5]. '#F27420' - оранжевый (ish) цвет узла, а цвета 'rgba(253, 227, 212, 0.5)', 'rgba(242, 116, 32, 1)' и 'rgba(253, 227, 212, 0.5)' описывают цвета ссылок от узла к некоторым целям. Пока что не использовалась информация из приведенного выше примера:

Пример данных 2 (частичный)

[-,-,--'-------','---------------','-------------------'],
[-,-,-,'#4994CE','Leave+No – 16','-------------------'],
[-,-,-,'#FABC13','Remain+Yes – 21','-------------------'],

И эта информация используется при отображении остальных элементов диаграммы.

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

Следующий образец данных создает диаграмму ниже, следуя логике, описанной выше:

Пример данных 3

data = [['Source','Target','Value','Color','Node, Label','Link Color'],
        [0,5,20,'#F27420','Remain+No – 28','rgba(253, 227, 212, 0.5)'],
        [0,6,3,'#4994CE','Leave+No – 16','rgba(242, 116, 32, 1)'],
        [0,7,5,'#FABC13','Remain+Yes – 21','rgba(253, 227, 212, 0.5)'],
        [1,5,14,'#7FC241','Leave+Yes – 14','rgba(219, 233, 246, 0.5)'],
        [1,6,1,'#D3D3D3','Didn’t vote in at least one referendum – 21','rgba(73, 148, 206, 1)']]

Снимок экрана 1. Частичный график с образцом данных 3

введите описание изображения здесь

ВОПРОС:

Добавление строки [1,7,1,'#8A5988','46 – No','rgba(219, 233, 246,0.5)'] в набор данных создает новую связь между источником [5] и целью [7], но одновременно применяет цвет и метку к цели 5. Я думаю, что следующий ярлык, который будет применен к диаграмме, будет 'Remain+Yes – 21', поскольку он не использовался. Но вот что происходит: метка '46 – No' применяется к цели 5. ПОЧЕМУ?

Снимок экрана 2. Частичный график с образцом данных 3 + [1,7,1,'#8A5988','46 – No','rgba(219, 233, 246,0.5)'] :

введите описание изображения здесь

И как определить, что является источником, а что целью, на основе этого фрейма данных?

Я знаю, что это странный вопрос, на который сложно ответить, но я надеюсь, что у кого-то есть предложение. Я также знаю, что фрейм данных может быть не лучшим источником для диаграммы Sankey. Может быть, вместо этого json?


Полный код и образец данных для простого копирования и вставки в блокнот Jupyter:


import pandas as pd
import numpy as np
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)

# Original data
data = [['Source','Target','Value','Color','Node, Label','Link Color'],
    [0,5,20,'#F27420','Remain+No – 28','rgba(253, 227, 212, 0.5)'],
    [0,6,3,'#4994CE','Leave+No – 16','rgba(242, 116, 32, 1)'],
    [0,7,5,'#FABC13','Remain+Yes – 21','rgba(253, 227, 212, 0.5)'],
    [1,5,14,'#7FC241','Leave+Yes – 14','rgba(219, 233, 246, 0.5)'],
    [1,6,1,'#D3D3D3','Didn’t vote in at least one referendum – 21','rgba(73, 148, 206, 1)'],
    [1,7,1,'#8A5988','46 – No','rgba(219, 233, 246,0.5)'],
    [2,5,3,'#449E9E','39 – Yes','rgba(250, 188, 19, 1)'],
    [2,6,17,'#D3D3D3','14 – Don’t know / would not vote','rgba(250, 188, 19, 0.5)'],
    [2,7,2,'','','rgba(250, 188, 19, 0.5)'],
    [3,5,3,'','','rgba(127, 194, 65, 1)'],
    [3,6,9,'','','rgba(127, 194, 65, 0.5)'],
    [3,7,2,'','','rgba(127, 194, 65, 0.5)'],
    [4,5,5,'','','rgba(211, 211, 211, 0.5)'],
    [4,6,9,'','','rgba(211, 211, 211, 0.5)'],
    [4,7,8,'','','rgba(211, 211, 211, 0.5)']
    ]



headers = data.pop(0)
df = pd.DataFrame(data, columns = headers)
scottish_df = df

data_trace = dict(
    type='sankey',
    domain = dict(
      x =  [0,1],
      y =  [0,1]
    ),
    orientation = "h",
    valueformat = ".0f",
    node = dict(
      pad = 10,
      thickness = 30,
      line = dict(
        color = "black",
        width = 0
      ),
      label =  scottish_df['Node, Label'].dropna(axis=0, how='any'),
      color = scottish_df['Color']
    ),
    link = dict(
      source = scottish_df['Source'].dropna(axis=0, how='any'),
      target = scottish_df['Target'].dropna(axis=0, how='any'),
      value = scottish_df['Value'].dropna(axis=0, how='any'),
      color = scottish_df['Link Color'].dropna(axis=0, how='any'),
  )
)

layout =  dict(
    title = "Scottish Referendum Voters who now want Independence",
    height = 772,
    font = dict(
      size = 10
    ),    
)

fig = dict(data=[data_trace], layout=layout)
iplot(fig, validate=False)

person vestland    schedule 22.03.2019    source источник


Ответы (1)


Эта проблема выглядит действительно странно, но только до тех пор, пока вы не проанализируете, как создается сюжет Санки в plotly:

Когда вы создаете участок Санки, вы отправляете на него:

  1. Список узлов
  2. Список ссылок

Эти списки связаны друг с другом. Когда вы создаете список узлов длиной 5, любое ребро будет знать о 0,1,2,3,4 в своих началах и концах. В вашей программе вы создаете узел неправильно - вы создаете список ссылок, а затем просматриваете его и создаете узлы. Посмотрите на свою диаграмму. Он имеет два черных узла с undefined внутри. И какова длина вашего набора данных ... Да, 5. Индексы ваших узлов заканчиваются на 4, и на самом деле целевые узлы не определены. Вы добавляете шестой список в свой набор данных и - бинго! - есть nodes[5] существует! Просто попробуйте добавить еще одну новую строку в свой набор данных:

[1,7,1,'#FF0000','WAKA','rgba(219, 233, 246,0.5)']

И вы увидите, что другая черная полоса перекрашивается в красный цвет. У вас есть пять узлов (потому что у вас 5 ссылок и вы создаете узел путем итерации для списка ссылок), но целевые индексы ссылок равны 5,6,7. Вы можете исправить это двумя способами:

  1. Измените Target в наборе данных на 2,3,4
  2. Создавать узлы и ссылки отдельно (правильно)

Надеюсь, я помог вам в вашей проблеме и в понимании создания сюжета (что важнее IMO).

Изменить: Вот пример создания отдельных узлов / ссылок (обратите внимание, что часть node в data_trace использует только данные nodes_df, часть link в data_trace использует только данные links_df, а длина nodes_df и links_df не равны):

import pandas as pd
import numpy as np
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)

nodes = [
    ['ID', 'Label', 'Color'],
    [0,'Remain+No – 28','#F27420'],
    [1,'Leave+No – 16','#4994CE'],
    [2,'Remain+Yes – 21','#FABC13'],
    [3,'Leave+Yes – 14','#7FC241'],
    [4,'Didn’t vote in at least one referendum – 21','#D3D3D3'],
    [5,'46 – No','#8A5988']
]
links = [
    ['Source','Target','Value','Link Color'],
    [0,3,20,'rgba(253, 227, 212, 0.5)'],
    [0,4,3,'rgba(242, 116, 32, 1)'],
    [0,2,5,'rgba(253, 227, 212, 0.5)'],
    [1,5,14,'rgba(219, 233, 246, 0.5)'],
    [1,3,1,'rgba(73, 148, 206, 1)'],
    [1,4,1,'rgba(219, 233, 246,0.5)'],
    [1,2,10,'rgba(8, 233, 246,0.5)'],
    [1,3,5,'rgba(219, 77, 246,0.5)'],
    [1,5,12,'rgba(219, 4, 246,0.5)']
]

nodes_headers = nodes.pop(0)
nodes_df = pd.DataFrame(nodes, columns = nodes_headers)
links_headers = links.pop(0)
links_df = pd.DataFrame(links, columns = links_headers)

data_trace = dict(
    type='sankey',
    domain = dict(
      x =  [0,1],
      y =  [0,1]
    ),
    orientation = "h",
    valueformat = ".0f",
    node = dict(
      pad = 10,
      thickness = 30,
      line = dict(
        color = "black",
        width = 0
      ),
      label =  nodes_df['Label'].dropna(axis=0, how='any'),
      color = nodes_df['Color']
    ),
    link = dict(
      source = links_df['Source'].dropna(axis=0, how='any'),
      target = links_df['Target'].dropna(axis=0, how='any'),
      value = links_df['Value'].dropna(axis=0, how='any'),
      color = links_df['Link Color'].dropna(axis=0, how='any'),
  )
)

layout =  dict(
    title = "Scottish Referendum Voters who now want Independence",
    height = 772,
    font = dict(
      size = 10
    ),    
)

fig = dict(data=[data_trace], layout=layout)
iplot(fig, validate=False)

Редактировать 2: Давайте погрузимся еще глубже :) Узлы и ссылки на Sankey-диаграммах почти полностью независимы. Единственная информация, которая их ограничивает - это индексы в source-target в ссылках. Таким образом, мы можем создать много узлов и никаких ссылок для них (просто замените им узлы / ссылки в коде Edit1):

nodes = [
    ['ID', 'Label', 'Color'],
    [0,'Remain+No – 28','#F27420'],
    [1,'Leave+No – 16','#4994CE'],
    [2,'Remain+Yes – 21','#FABC13'],
    [3,'Leave+Yes – 14','#7FC241'],
    [4,'Didn’t vote in at least one referendum – 21','#D3D3D3'],
    [5,'46 – No','#8A5988'],
    [6,'WAKA1','#8A5988'],
    [7,'WAKA2','#8A5988'],
    [8,'WAKA3','#8A5988'],
    [9,'WAKA4','#8A5988'],
    [10,'WAKA5','#8A5988'],
    [11,'WAKA6','#8A5988'],

]
links = [
    ['Source','Target','Value','Link Color'],
    [0,3,20,'rgba(253, 227, 212, 0.5)'],
    [0,4,3,'rgba(242, 116, 32, 1)'],
    [0,2,5,'rgba(253, 227, 212, 0.5)'],
    [1,5,14,'rgba(219, 233, 246, 0.5)'],
    [1,3,1,'rgba(73, 148, 206, 1)'],
    [1,4,1,'rgba(219, 233, 246,0.5)'],
    [1,2,10,'rgba(8, 233, 246,0.5)'],
    [1,3,5,'rgba(219, 77, 246,0.5)'],
    [1,5,12,'rgba(219, 4, 246,0.5)']
]

И эти узлы не появятся на диаграмме.

Мы можем создавать только ссылки без узлов:

nodes = [
    ['ID', 'Label', 'Color'],
]
links = [
    ['Source','Target','Value','Link Color'],
    [0,3,20,'rgba(253, 227, 212, 0.5)'],
    [0,4,3,'rgba(242, 116, 32, 1)'],
    [0,2,5,'rgba(253, 227, 212, 0.5)'],
    [1,5,14,'rgba(219, 233, 246, 0.5)'],
    [1,3,1,'rgba(73, 148, 206, 1)'],
    [1,4,1,'rgba(219, 233, 246,0.5)'],
    [1,2,10,'rgba(8, 233, 246,0.5)'],
    [1,3,5,'rgba(219, 77, 246,0.5)'],
    [1,5,12,'rgba(219, 4, 246,0.5)']
]

И у нас будут только ссылки из ниоткуда в никуда.

Если вы хотите добавить (1) новый источник со ссылкой, вам следует добавить новый список в nodes, вычислить его индекс (вот почему у меня есть столбец ID) и добавить новый список в links с Source равным индексу узла.

Если вы хотите добавить (2) новую цель для существующих узлов - просто добавьте новый список в links и напишите его Source и Target правильно:

    [1,100500,10,'rgba(219, 233, 246,0.5)'],
    [1,100501,10,'rgba(8, 233, 246,0.5)'],
    [1,100502,10,'rgba(219, 77, 246,0.5)'],
    [1,100503,10,'rgba(219, 4, 246,0.5)']

(Здесь я создал 4 новых ссылки для 4 новых целей. Источник - это узел с индексом 1 для всех из них).

(3 + 4): диаграммы Санки не различаются по источникам и целям. Все они просто узлы для Санки. Каждый узел может быть как источником, так и целью. Посмотри на это:

nodes = [
    ['ID', 'Label', 'Color'],
    [0,'WAKA WANNA BE SOURCE','#F27420'],
    [1,'WAKA WANNA BE TARGET','#4994CE'],
    [2,'WAKA DON\'T KNOW WHO WANNA BE','#FABC13'],

]
links = [
    ['Source','Target','Value','Link Color'],
    [0,1,10,'rgba(253, 227, 212, 1)'],
    [0,2,10,'rgba(242, 116, 32, 1)'],
    [2,1,10,'rgba(253, 227, 212, 1)'],
]

Здесь у вас будет трехколоночная диаграмма Санки. Узел 0 - это источник, 1 - цель, а узел 2 - источник для 1 и цель для 2.

person vurmux    schedule 26.03.2019
comment
Спасибо, что ответили! Не могли бы вы показать, как именно вы поступите 2. Create nodes and links separately (right way)? - person vestland; 27.03.2019
comment
Добавил весь скрипт. Вы можете скопировать и вставить его в блокнот Jupyter и проверить. - person vurmux; 27.03.2019
comment
Прохладный! Итак, как бы вы (1) добавили еще один источник с одной или несколькими ссылками? (2) добавить еще одну цель? (3) добавить узел И цель? (4) добавить узел, который одновременно является целью и источником? Надеюсь, вы не будете возражать, если сделаете несколько последующих шагов, но я полностью понимаю, если вы тоже не найдете для этого времени. - person vestland; 27.03.2019
comment
Обновился еще раз :) - person vurmux; 27.03.2019