Используйте третий столбец значений, чтобы отобразить градиент цвета на график столбца 1 и столбца 2; Matplotlib - PullRequest
1 голос
/ 09 февраля 2020

У меня есть датафрейм с 3 столбцами. Я использую df ['name'] в качестве оси x и df ['value1'] в качестве оси y. Я хотел бы использовать 3-й столбец (df ['value2']) для создания цветового градиента для барплота.

Используемые данные

df ['name '] это имена. df ['value1'] являются первичными измерениями. df ['value2'] - это метрики достоверности для значений df ['value1']. Я думаю, что отображение df ['value2'] в цветном градиенте дало бы мощный визуальный эффект.

Проблемы

Я предпринял множество способов достижения этого с помощью matplotlib. модули цвета, но ничего не дало результата, который я ищу. Вот база кода (без отображения цвета в df ['value2']).

INPUT

import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
import matplotlib.colors as colors


df = {'name': ['a','b','c','d'], 'value1': [10.1,13.3,9.5,15.1], 'value2': [1.5,2.0,3.5,1.3]}
df = pd.DataFrame(df)
print(df)


colorbar_1 = LinearSegmentedColormap.from_list('colorbar', ['#990000','#FF6666'], N=100)
df[['name','value1']].plot(kind='bar', colormap=colorbar_1 ,width=0.8, ylim=[9,16], fontsize=5)
plt.xticks(df.index,df['name'].values, rotation=90)
plt.ylim([9,16])
plt.gcf().subplots_adjust(bottom=0.15)
plt.tight_layout()
plt.savefig('test.png',dpi=600)
plt.show()

OUTPUT

  name  value1  value2
0    a    10.1     1.5
1    b    13.3     2.0
2    c     9.5     3.5
3    d    15.1     1.3

Результат: 'test.png'

Требуемый формат

['Desired-format'

I также создали 4-й столбец с сгенерированным шестнадцатеричным кодом, который можно использовать для выполнения поставленной задачи Я предполагаю, что есть более простые методы включения df ['value2'], но сгенерированный шестнадцатеричный код может быть полезен?

INPUT

import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
import matplotlib.colors as colors

df = {'name': ['a','b','c','d'], 'value1': [10.1,13.3,9.5,15.1], 'value2': [1.5,2.0,3.5,1.3]}
df = pd.DataFrame(df)
print(df)

norm = mpl.colors.Normalize(vmin=0, vmax=16, clip=True)
mapper = plt.cm.ScalarMappable(norm=norm, cmap=plt.cm.viridis)
df['hex_code'] = df['value2'].apply(lambda x: colors.to_hex(mapper.to_rgba(x)))
print(df)

OUTPUT

  name  value1  value2
0    a    10.1     1.5
1    b    13.3     2.0
2    c     9.5     3.5
3    d    15.1     1.3
  name  value1  value2 hex_code
0    a    10.1     1.5  #482374
1    b    13.3     2.0  #472d7b
2    c     9.5     3.5  #3e4989
3    d    15.1     1.3  #481d6f

1 Ответ

1 голос
/ 09 февраля 2020

Одна из проблем заключается в том, что использование df[['name', 'value1']].plot заставляет pandas думать, что вам нужно два столбца в строке в кадре данных, один столбец для «имени» и один для «значения1». И тогда pandas запутывается, так как не может нарисовать полосу для «имени» и пропускает их. Затем, для параметра color=, pandas теперь думает, что первый цвет в списке цветов для 'name', а второй для 'value1'.

Итак, для начала вам нужно вызвать это как df['value1'].plot(). Тогда pandas действительно любит использовать индекс для оси X. Чтобы в качестве индекса использовалось имя, просто сделайте его индексом вашего фрейма данных.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, LinearSegmentedColormap, Normalize

df = {'name': ['a','b','c','d'], 'value1': [10.1,13.3,9.5,15.1], 'value2': [1.5,2.0,3.5,1.3]}
df = pd.DataFrame(df)
df.set_index('name', inplace=True)
print(df)

colormap_1 = LinearSegmentedColormap.from_list('colorbar', ['#990000','#FF6666'], N=100)
norm = Normalize(vmin=min(df['value2']), vmax=max(df['value2']))
colors = [colormap_1(norm(v)) for v in df['value2']]
df['value1'].plot(kind='bar', color=colors, width=0.8, ylim=[9,16], fontsize=5)
plt.xticks(rotation=90)
plt.ylim([9,16])

plt.tight_layout()
plt.savefig('test.png',dpi=600)
plt.show()

example plot

PS: чтобы получить что-то похожее на ваше связанное изображение (таким образом, не используя 'value2'), вы можете поэкспериментировать с:

# same dataframe as before, with 'name' as index
ymin = 9
ymax = 16
barplot = df['value1'].plot(kind='bar', width=0.8, ylim=[ymin, ymax], fontsize=5)
plt.xticks(rotation=90)

gradient = np.linspace(1,0,256).reshape(256,1)
for bar in barplot.containers[0]:
    bar.set_facecolor("none")
    x, y = bar.get_xy()
    w, h = bar.get_width(), bar.get_height()
    plt.imshow(gradient, extent=[x, x + w, y + ymin, y + h], aspect="auto", cmap='bone')
plt.imshow(gradient, extent=[*plt.xlim(), *plt.ylim()] ,aspect="auto", cmap='copper_r', zorder=-1)

plt.tight_layout()
#plt.savefig('test.png',dpi=600)
plt.show()
...