Ошибка значения из-за отсутствия элемента в цветовой карте

#python #pandas #matplotlib #networkx

Вопрос:

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

 import networkx as nx
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt, colors as mcolor

# Sample DataFrames
df1 = pd.DataFrame({
    'Node': ['A', 'A', 'B', 'B', 'B', 'Z'],
    'Edge': ['B', 'D', 'N', 'A', 'X', 'C']
})
df2 = pd.DataFrame({
    'Nodes': ['A', 'B', 'C', 'D', 'N', 'S', 'X'],
    'Attribute': [-1, 0, -1.5, 1, 1, 9, 0]
})

# Simplified construction of `colour_map`
uni_val = df2['Attribute'].unique()
colors = plt.cm.jet(np.linspace(0, 1, len(uni_val)))
# Map colours to_hex then zip with
mapper = dict(zip(uni_val, map(mcolor.to_hex, colors)))
color_map =df2.set_index('Nodes')['Attribute'].map(mapper).fillna('black')


G = nx.from_pandas_edgelist(df1, source='Node', target='Edge')
# Add Attribute to each node
nx.set_node_attributes(G, color_map, name="colour")

# Then draw with colours based on attribute values:
nx.draw(G,
        node_color=nx.get_node_attributes(G, 'colour').values(),
        with_labels=True)

plt.show()
 

Z не df2 потому df2 , что был создан с учетом только значений, отличных от NA.
Я хотел бы присвоить черный цвет немаркированным узлам, т. Е. тем узлам, которые не находятся внутри df2 .
Пытаясь запустить приведенный выше код, я получаю эту ошибку:

 ValueError: 'c' argument has 7 elements, which is inconsistent with 'x' and 'y' with size 8.
 

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

Ответ №1:

Так Z как не входит df2 , но является одним из узлов, мы должны, вместо того, чтобы создавать свойства исключительно из df2 , мы должны reindex color_map из nodes узлов с fill_value :

 # Create graph before color map:
G = nx.from_pandas_edgelist(df1, source='Node', target='Edge')
# Create Colour map. Ensure all nodes have a value via reindex using nodes
color_map = (
    df2.set_index('Nodes')['Attribute'].map(mapper)
        .reindex(G.nodes(), fill_value='black')
)
 

график по умолчанию


color_map без переиндексации

 df2.set_index('Nodes')['Attribute'].map(mapper)

Nodes
A    #000080
B    #0080ff
C    #7dff7a
D    #ff9400
N    #ff9400
S    #800000
X    #0080ff
Name: Attribute, dtype: object
 

nodes (используя узлы здесь, так как это будут все узлы в графике, а не только те, в df2 которых )

 G.nodes()

['A', 'B', 'D', 'N', 'X', 'Z', 'C']
 

reindex чтобы убедиться, что все узлы присутствуют в отображении:

 df2.set_index('Nodes')['Attribute'].map(mapper).reindex(G.nodes(), fill_value='black')

Nodes
A    #000080
B    #0080ff
D    #ff9400
N    #ff9400
X    #0080ff
Z      black  # <- Missing Nodes are added with specified value
C    #7dff7a
Name: Attribute, dtype: object
 

Полный Код:

 import networkx as nx
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt, colors as mcolor

# Sample DataFrames
df1 = pd.DataFrame({
    'Node': ['A', 'A', 'B', 'B', 'B', 'Z'],
    'Edge': ['B', 'D', 'N', 'A', 'X', 'C']
})
df2 = pd.DataFrame({
    'Nodes': ['A', 'B', 'C', 'D', 'N', 'S', 'X'],
    'Attribute': [-1, 0, -1.5, 1, 1, 9, 0]
})

# Simplified construction of `colour_map`
uni_val = df2['Attribute'].unique()
colors = plt.cm.jet(np.linspace(0, 1, len(uni_val)))
# Map colours to_hex then zip with
mapper = dict(zip(uni_val, map(mcolor.to_hex, colors)))

G = nx.from_pandas_edgelist(df1, source='Node', target='Edge')
# Create Colour map. Ensure all nodes have a value via reindex
color_map = (
    df2.set_index('Nodes')['Attribute'].map(mapper)
        .reindex(G.nodes(), fill_value='black')
)
# Add Attribute to each node
nx.set_node_attributes(G, color_map, name="colour")

# Then draw with colours based on attribute values:
nx.draw(G,
        node_color=nx.get_node_attributes(G, 'colour').values(),
        with_labels=True)

plt.show()
 

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

1. Большое тебе спасибо, Генри! Как всегда, отличный и хорошо объясненный ответ!

2. извините, Генри, в конце запуска с полным набором данных я получил эту ошибку: ValueError: 'c' argument must be a color, a sequence of colors, or a sequence of numbers, not dict_values . Ты знаешь, почему? ----> 6 nx.draw(G, node_color=nx.get_node_attributes(G, 'colour').values(),with_labels=True)

3. Ты можешь попробовать node_color=list(nx.get_node_attributes(G, 'colour').values()) вместо этого. Я не знаю, какая версия matplotlib начала разрешать разбрасывать любую коллекцию.

4. к сожалению, он возвращает ту же ошибку. могу я спросить вас, какую версию matplotlib вы используете? Мой-3.3.4

5. Эта версия должна работать нормально. Эта ошибка также может возникнуть, если у вас в качестве атрибута указан недопустимый цветовой код. Например, изменение на fill_value='nonsense' в приведенном выше коде приведет к этой ошибке. Дважды проверьте свои color_map и убедитесь, что все они соответствуют действительным шестнадцатеричным или цветовым кодам.