Управление связями Sankey

Я пытаюсь контролировать, какие потоки соединяются друг с другом, используя диаграмму Matplotlib Sankey. Я изменяю пример двух базовых систем.

Я думаю, что мое замешательство сводится к непониманию того, что это на самом деле означает:

Обратите внимание, что указано только одно соединение, но системы образуют схему, поскольку: (1) длины путей оправданы и (2) зеркалирование ориентации и упорядочения потоков.

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

import numpy as np
import matplotlib.pyplot as plt

from matplotlib.sankey import Sankey

plt.rcParams["figure.figsize"] = (15,10)


system_1 = [
    {"label": "1st",  "value":  2.00, "orientation":  0},
    {"label": "2nd",  "value":  0.15, "orientation": -1},
    {"label": "3rd",  "value":  0.60, "orientation": -1},
    {"label": "4th",  "value": -0.10, "orientation": -1},
    {"label": "5th",  "value":  0.25, "orientation": -1},
    {"label": "6th",  "value":  0.25, "orientation": -1},
    {"label": "7th",  "value":  0.25, "orientation": -1},
    {"label": "8th",  "value":  0.25, "orientation": -1},
    {"label": "9th",  "value":  0.25, "orientation": -1}
]

system_2 = system_1[:4]
system_2.append({"label": "new",  "value":  -0.25, "orientation": 1})


fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], title="Where are all my cows?")
flows  = [x["value"] for x in system_1]
labels = [x["label"] for x in system_1]
orientations=[x["orientation"] for x in system_1]
sankey = Sankey(ax=ax, unit="cow")
sankey.add(flows=flows, 
           labels=labels,
           label='one',
           orientations=orientations)

sankey.add(flows=[-x["value"] for x in system_2], 
           labels=[x["label"] for x in system_2],
           label='two',
           orientations=[-x["orientation"] for x in system_2], 
           prior=0, 
           connect= (0,0)
          )

diagrams = sankey.finish()
diagrams[-1].patch.set_hatch('/')
plt.legend(loc='best')


plt.show()

Это дает мне:

A sankey diagram that doesn't really work

Он должен объединить потоки с соответствующими ярлыками.

Я читал это и это, но они не помогают мне понять, что на самом деле происходит.

Ответ 1

Пусть начнется, пытаясь решить путаницу

Я думаю, что мое замешательство сводится к непониманию того, что это на самом деле означает:

Обратите внимание, что указано только одно соединение, но системы образуют схему, поскольку: (1) длины путей оправданы и (2) зеркалирование ориентации и упорядочения потоков.

(2) Ориентация и упорядочение потоков зеркалируются.

То, что вы, вероятно, понимаете неправильно, - это значение зеркального отображения, которое в этом случае действительно запутывает. Можно было бы подумать, что зеркальное отображение равно инвертированному, но это отчасти верно:
flows (или, как вы его называете, в вашем коде: values) должны быть инвертированы, это вы получили правильно. Поскольку values соответствуют входам (value > 0) или выходу (value < 0). И только выход может быть подключен к входу, и наоборот.

Но orientation должна быть одинаковой для обоих потоков, которые вы пытаетесь подключить. Этот не инвертирован, но он все равно должен быть "зеркальным". Что с этим связано? Ну, если I/O смотрит в направлении стрелки, из которой он пришел, ему нужно увидеть другой ввод-вывод (как при взгляде в зеркало), только тогда они смогут соединиться. Это не так просто объяснить как не родной оратор, но я попытаюсь проиллюстрировать идею:

Able to connect:         Not able to connect:        Not able to connect:
I/O  Mirror  I/O         I/O  Mirror  I/O            I/O  Mirror  I/O
╚══>   |    >══╝          ╗     |      ╔                    |      ║
                          ║     |      ║             ══>    |      ║
                          v     |      ^                    |      ^

В вашем коде вы перевернули orientation. Вот почему, например, 3-й поток оранжевой системы находится в верхнем левом углу, а его аналог синей системы находится в правом нижнем углу. Нет никакого способа, чтобы эти входы/выходы могли "видеть" друг друга.

Вы можете вернуть инверсию второй системы, удалив - которая добавляет ориентацию x:

orientations=[x["orientation"] for x in system_2]

Вы увидите, что потоки близки друг к другу, но вы находитесь в ситуации, как показано в разделе "Невозможно Not able to connect -illustration (№ 2). Это означает, что структура вашей диаграммы не сможет работать так. Вы можете сгибать одиночные потоки только в этих трех направлениях: -90 °, 0 ° или 90 °. Какие корреспонденты orientations = -1, 0 or 1. Единственный способ напрямую подключить эти потоки - установить их orientation=0, но мне кажется, что это не ваша цель.

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

(1) Длины путей оправданы.

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

Код и вывод

import numpy as np
import matplotlib.pyplot as plt

from matplotlib.sankey import Sankey

plt.rcParams["figure.figsize"] = (15,10)


system_1 = [
    {"label": "1st",  "value": -2.00, "orientation":  1},
    {"label": "4th",  "value":  0.10, "orientation":  1},
    {"label": "2nd",  "value":  0.15, "orientation":  1},
    {"label": "3rd",  "value":  0.60, "orientation":  1},
    {"label": "5th",  "value":  0.25, "orientation": -1},
    {"label": "6th",  "value":  0.25, "orientation": -1},
    {"label": "7th",  "value":  0.25, "orientation":  1},
    {"label": "8th",  "value":  0.25, "orientation":  1},
    {"label": "9th",  "value":  0.25, "orientation":  0}
]

system_2 = [
    {"label": "1st",  "value":  2.00, "orientation":  1},
    {"label": "4th",  "value": -0.10, "orientation":  1},
    {"label": "2nd",  "value": -0.15, "orientation":  1},
    {"label": "3rd",  "value": -0.60, "orientation":  1},
    {"label": "new",  "value": -0.25, "orientation":  1}
]

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], title="Where are all my cows?")

flows_1  = [x["value"] for x in system_1]
labels_1 = [x["label"] for x in system_1]
orientations_1=[x["orientation"] for x in system_1]

flows_2  = [x["value"] for x in system_2]
labels_2 = [x["label"] for x in system_2]
orientations_2=[x["orientation"] for x in system_2]

sankey = Sankey(ax=ax, unit=None)
sankey.add(flows=flows_1, 
           labels=labels_1,
           label='one',
           orientations=orientations_1)

sankey.add(flows=flows_2, 
           labels=labels_2,
           label='two',
           orientations=orientations_2,
           pathlengths=[0, 0.4, 0.5, 0.65, 1.25],
           prior=0,
           connect=(0,0))

diagrams = sankey.finish()
diagrams[-1].patch.set_hatch('|')
diagrams[-0].patch.set_hatch('-')
plt.legend(loc='best')


plt.show()

Output