Is it possible to get an idea of what the color will appear in a plot by supplying the original RGB value (or other colormaps like RdYlBu) in python? Currently I have to really first convert a float number using
cm.RdYlBu(x)
and make a plot to see how the colors show up. But is there a quick way to check the color? Not necessarily in python, but any website that takes the value and returns the color according to the colormap would be fine.
Quick check in python console
You may use python itself to quickly show a figure with the respective color from the matplotlib colormap.
E.g. write a function
def c(x):
col = plt.cm.RdYlBu(x)
fig, ax = plt.subplots(figsize=(1,1))
fig.set_facecolor(col)
ax.axis("off")
plt.show()
and then call it with the number of interest, e.g. c(0.4).
Example how that looks in a jupyter console:
A colormap viewer
Of course one could also write some color map viewer with matplotlib. For example:
import sys
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.widgets
class CMViewer():
def __init__(self, cmap, valinit=0.5):
self.cmap = plt.get_cmap(cmap)
col = self.cmap(valinit)
self.fig, axes = plt.subplots(nrows=4, figsize=(5,3), num=cmap,
gridspec_kw={"height_ratios" : [0.3,1,1,1]})
self.fig.subplots_adjust(right=0.85)
self.sliderax, self.cax, self.fax, self.lax = axes
self.slider = matplotlib.widgets.Slider(self.sliderax, cmap, 0., 1.,
valinit=valinit)
self.slider.on_changed(self.update)
self.slider.vline.set_color("k")
x = np.linspace(0.,1.,256)
self.cax.imshow(x[np.newaxis, :], cmap=cmap, aspect="auto")
ta = dict(axis="both", bottom=0, left=0, labelbottom=0, labelleft=0)
self.cax.tick_params(**ta)
self.fax.tick_params(**ta)
self.lax.tick_params(**ta)
x = np.array([0,0,1,2,2])
self.lax.imshow(x[np.newaxis, :], cmap="gray", aspect="auto",
extent=[0,1,-0.2,1.2])
self.lines = []
for i in range(5):
line = self.lax.plot([0,1],[i/4.,i/4.], alpha=0.2+i/5., color=col)[0]
self.lines.append(line)
self.update(0)
plt.show()
def fmtcol(self, col):
return "{:.3f} | {:.3f} | {:.3f}".format(*col)
def update(self, val):
x = self.slider.val
col = self.cmap(x)
self.fax.set_facecolor(col)
for line in self.lines:
line.set_color(col)
self.slider.poly.set_color(col)
self.slider.vline.set_xdata(x)
self.lax.set_xlabel(self.fmtcol(col))
self.fig.canvas.draw_idle()
if __name__ == "__main__":
if len(sys.argv) > 1:
CMViewer(sys.argv[1])
else:
CMViewer("RdYlBu")
If this is saved as CMViewer.py it could be run as script like
> python CMViewer.py
or with a colormap specified,
> python CMViewer.py viridis
Look at the color converter here. It takes RGB values as input.
The matplotlib docs have a colormap reference guide here:
https://matplotlib.org/stable/gallery/color/colormap_reference.html
Here's a screenshot for the diverging colormaps, which contains RdYlBu.
Related
I am trying to create an interactive bar chart where the bars of the chart change color when the user selects a value (based on mouse click). The selected value displays at the bottom of the chart and the bars are supposed to change color dependent on the probability of the selected value being above or below the mean of the a sample.
I am stuck on the coloring of the bars. When I click on the chart only the first bar changes colors and then does not update with subsequent clicks.
Overall expected result is to allow multiple values to be selected based on mouse click events. Intention is then to draw the horizontal line at the selected value and then recolor the bars based on the probability of the selected value being within the range of the sample mean. This is being run in jupyter.
I am still new to this so certainly appreciate any advice that you may have.
import numpy as np
from scipy import stats
from scipy.stats import norm
import math
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import ipywidgets as wdg
from matplotlib.cm import ScalarMappable
%matplotlib notebook
###Set up dummy data
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000,200000,3650),
np.random.normal(43000,100000,3650),
np.random.normal(43500,140000,3650),
np.random.normal(48000,70000,3650)],
index=[1992,1993,1994,1995])
###Calculate statistics incl confidence interval for the mean. Calculate 97.5% interquantile range of the normal distribution (being 1.96 x standard error)
df = df.T
stats = df.describe(percentiles = [0.025, 0.25, 0.5, 0.75, 0.975])
mean = stats.loc['mean']
onesd_meanerror = df.sem(axis = 0)
error_low = onesd_meanerror*1.96
error_high = onesd_meanerror*1.96
###Setup initial chart and plot bar chart
fig = plt.figure()
ax = fig.add_subplot(111)
x_axis_label = df.columns.values
plt.xticks(x_axis_label)
bars = (ax.bar(x_axis_label, mean, width=0.85, alpha=0.9, align='center',
yerr = (error_low, error_high), error_kw={'capsize': 10, 'elinewidth': 2, 'alpha':1}))
###Create and display textarea widget
txt = wdg.Textarea(
value='',
placeholder='',
description='Y Value:',
disabled=False)
display(txt)
### Formats color bar. Need the scalar mapable to enable use of the color bar.
my_cmap = plt.cm.get_cmap('coolwarm')
sm = ScalarMappable(cmap=my_cmap, norm=plt.Normalize(0,1))
sm.set_array([])
cbar = plt.colorbar(sm)
cbar.set_label('Probability', rotation=270,labelpad=25)
ydataselect = 40000
class ClickChart(object):
def __init__(self, ax):
self.fig=ax.figure
self.ax = ax
self.horiz_line = ax.axhline(y=ydataselect, color='black', linewidth=2)
self.fig.canvas.mpl_connect('button_press_event', self.onclick)
### Event handlers
def onclick(self, event):
self.horiz_line.remove()
self.ypress = event.ydata
self.horiz_line = ax.axhline(y=self.ypress, color='red', linewidth=2)
txt.value = str(event.ydata)
self.color_bar(event)
def color_bar(self, event):
for index, bar in enumerate(bars):
bar.set_color(c=my_cmap(self.calc_prob(index)))
print(index)
def calc_prob(self, index):
global mean, onesd_meanerror
mean = mean.iloc[index]
err = onesd_meanerror.iloc[index]
result = norm.cdf(self.ypress, loc=mean, scale=err)
return result
click = ClickChart(ax)```
You are so close! The problem is you are re-defining mean inside of calc_prob(). Making changes to avoid this will fix the code and give the behavior you want:
def calc_prob(self, index):
global mean, onesd_meanerror
mean2 = mean.iloc[index] # Changed
err = onesd_meanerror.iloc[index]
result = norm.cdf(self.ypress, loc=mean2, scale=err) # Changed
return result
I try to create a color theme to be used with matplotlib and it works fine with pie charts but I get an error message saying that it is an invalid RGBA argument when running plt.plot(x,y,color = color_theme)
Works
import matplotlib.pyplot as plt
color_theme = ['#998166', '#bacfd9', '#bfbaa6', '#a0bab8', '#63605b',
'#8f8a83', '#bdb6af', '#e8e5e3', '#634632']
x = list(range(1,10))
y = [1,2,3,4,.5,4,3,2,1]
plt.pie(x, colors = color_theme)
Do not work
import matplotlib.pyplot as plt
color_theme = ['#998166', '#bacfd9', '#bfbaa6', '#a0bab8', '#63605b',
'#8f8a83', '#bdb6af', '#e8e5e3', '#634632']
x = list(range(1,10))
y = [1,2,3,4,.5,4,3,2,1]
plt.plot(x,y,color = color_theme)
Leaving out the color argument works as well.
What am I missing here?
Using plt.plot plots a line. You only have 1 line in the plot, therefore you can only give one value in the color= argument (you can have a line with multiple colors, there are other questions on SO about that).
You can choose one element of your color_theme list and choose that
plt.plot(x,y,color = color_theme[0]) # uses '#998166' for the color.
You might want to use a scatter plot which can accept a list of colors:
color_theme = ['#998166', '#bacfd9', '#bfbaa6', '#a0bab8', '#63605b',
'#8f8a83', '#bdb6af', '#e8e5e3', '#634632']
x = list(range(1,10))
y = [1,2,3,4,.5,4,3,2,1]
plt.scatter(x, y, color = color_theme)
plt.show()
I am translating a set of R visualizations to Python. I have the following target R multiple plot histograms:
Using Matplotlib and Seaborn combination and with the help of a kind StackOverflow member (see the link: Python Seaborn Distplot Y value corresponding to a given X value), I was able to create the following Python plot:
I am satisfied with its appearance, except, I don't know how to put the Header information in the plots. Here is my Python code that creates the Python Charts
""" Program to draw the sampling histogram distributions """
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import seaborn as sns
def main():
""" Main routine for the sampling histogram program """
sns.set_style('whitegrid')
markers_list = ["s", "o", "*", "^", "+"]
# create the data dataframe as df_orig
df_orig = pd.read_csv('lab_samples.csv')
df_orig = df_orig.loc[df_orig.hra != -9999]
hra_list_unique = df_orig.hra.unique().tolist()
# create and subset df_hra_colors to match the actual hra colors in df_orig
df_hra_colors = pd.read_csv('hra_lookup.csv')
df_hra_colors['hex'] = np.vectorize(rgb_to_hex)(df_hra_colors['red'], df_hra_colors['green'], df_hra_colors['blue'])
df_hra_colors.drop(labels=['red', 'green', 'blue'], axis=1, inplace=True)
df_hra_colors = df_hra_colors.loc[df_hra_colors['hra'].isin(hra_list_unique)]
# hard coding the current_component to pc1 here, we will extend it by looping
# through the list of components
current_component = 'pc1'
num_tests = 5
df_columns = df_orig.columns.tolist()
start_index = 5
for test in range(num_tests):
current_tests_list = df_columns[start_index:(start_index + num_tests)]
# now create the sns distplots for each HRA color and overlay the tests
i = 1
for _, row in df_hra_colors.iterrows():
plt.subplot(3, 3, i)
select_columns = ['hra', current_component] + current_tests_list
df_current_color = df_orig.loc[df_orig['hra'] == row['hra'], select_columns]
y_data = df_current_color.loc[df_current_color[current_component] != -9999, current_component]
axs = sns.distplot(y_data, color=row['hex'],
hist_kws={"ec":"k"},
kde_kws={"color": "k", "lw": 0.5})
data_x, data_y = axs.lines[0].get_data()
axs.text(0.0, 1.0, row['hra'], horizontalalignment="left", fontsize='x-small',
verticalalignment="top", transform=axs.transAxes)
for current_test_index, current_test in enumerate(current_tests_list):
# this_x defines the series of current_component(pc1,pc2,rhob) for this test
# indicated by 1, corresponding R program calls this test_vector
x_series = df_current_color.loc[df_current_color[current_test] == 1, current_component].tolist()
for this_x in x_series:
this_y = np.interp(this_x, data_x, data_y)
axs.plot([this_x], [this_y - current_test_index * 0.05],
markers_list[current_test_index], markersize = 3, color='black')
axs.xaxis.label.set_visible(False)
axs.xaxis.set_tick_params(labelsize=4)
axs.yaxis.set_tick_params(labelsize=4)
i = i + 1
start_index = start_index + num_tests
# plt.show()
pp = PdfPages('plots.pdf')
pp.savefig()
pp.close()
def rgb_to_hex(red, green, blue):
"""Return color as #rrggbb for the given color values."""
return '#%02x%02x%02x' % (red, green, blue)
if __name__ == "__main__":
main()
The Pandas code works fine and it is doing what it is supposed to. It is my lack of knowledge and experience of using 'PdfPages' in Matplotlib that is the bottleneck. How can I show the header information in Python/Matplotlib/Seaborn that I can show in the corresponding R visalization. By the Header information, I mean What The R visualization has at the top before the histograms, i.e., 'pc1', MRP, XRD,....
I can get their values easily from my program, e.g., current_component is 'pc1', etc. But I don't know how to format the plots with the Header. Can someone provide some guidance?
You may be looking for a figure title or super title, fig.suptitle:
fig.suptitle('this is the figure title', fontsize=12)
In your case you can easily get the figure with plt.gcf(), so try
plt.gcf().suptitle("pc1")
The rest of the information in the header would be called a legend.
For the following let's suppose all subplots have the same markers. It would then suffice to create a legend for one of the subplots.
To create legend labels, you can put the labelargument to the plot, i.e.
axs.plot( ... , label="MRP")
When later calling axs.legend() a legend will automatically be generated with the respective labels. Ways to position the legend are detailed e.g. in this answer.
Here, you may want to place the legend in terms of figure coordinates, i.e.
ax.legend(loc="lower center",bbox_to_anchor=(0.5,0.8),bbox_transform=plt.gcf().transFigure)
I'm trying to do a quite simple scatter plot with error bars and semilogy scale. What is a little bit different from tutorials I have found is that the color of the scatterplot should trace a different quantity. On one hand, I was able to do a scatterplot with the errorbars with my data, but just with one color. On the other hand, I realized a scatterplot with the right colors, but without the errorbars.
I'm not able to combine the two different things.
Here an example using fake data:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
##--------------
##important part of the script
##this works, but all the dots are of the same color
#plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,fmt='o')
##this is what is should be in terms of colors, but it is without the error bars
#plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
##what I tried (and failed)
plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,\
color=colors, fmt='o')
ax = plt.gca()
ax.set_yscale('log')
plt.show()
I even tried to plot the scatterplot after the errorbar, but for some reason everything plotted on the same window is put in background with respect to the errorplot.
Any ideas?
Thanks!
You can set the color to the LineCollection object returned by the errorbar as described here.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
sct = plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
cb = plt.colorbar(sct)
_, __ , errorlinecollection = plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error, marker = '', ls = '', zorder = 0)
error_color = sct.to_rgba(colors)
errorlinecollection[0].set_color(error_color)
errorlinecollection[1].set_color(error_color)
ax = plt.gca()
ax.set_yscale('log')
plt.show()
I have a bar plot and I want to get its colors and x/y values. Here is a sample code:
import matplotlib.pyplot as plt
def main():
x_values = [1,2,3,4,5]
y_values_1 = [1,2,3,4,5]
y_values_2 = [2,4,6,8,10]
f, ax = plt.subplots(1,1)
ax.bar(x_values,y_values_2,color='r')
ax.bar(x_values,y_values_1,color='b')
#Any methods?
plt.show()
if __name__ == '__main__':
main()
Are there any methods like ax.get_xvalues(), ax.get_yvalues(), ax.get_colors(), which I can use so I could extract back from ax the lists x_values, y_values_1, y_values_2 and the colors 'r' and 'b'?
The ax knows what geometric objects it's drawing, but nothing about it keeps track of when those geometric objects were added, and of course it doesn't know what they "mean": which patch comes from which bar-plot, etc. The coder needs to keep track of that to re-extract the right parts for further use. The way to do this is common to many Python programs: the call to barplot returns a BarContainer, which you can name at the time and use later:
import matplotlib.pyplot as plt
def main():
x_values = [1,2,3,4,5]
y_values_1 = [1,2,3,4,5]
y_values_2 = [2,4,6,8,10]
f, ax = plt.subplots(1,1)
rbar = ax.bar(x_values,y_values_2,color='r')
bbar = ax.bar(x_values,y_values_1,color='b')
return rbar, bbar
if __name__ == '__main__':
rbar, bbar = main()
# do stuff with the barplot data:
assert(rbar.patches[0].get_facecolor()==(1.0,0.,0.,1.))
assert(rbar.patches[0].get_height()==2)
A slight variation to the above answers to put it all within the call to another plotting command:
# plot various patch objects to ax2
ax2 = plt.subplot(1,4,2)
ax2.hist(...)
# start a new plot with same colors as i'th patch object
ax3 = plt.subplot(1,4,3)
plot(...,...,color=ax2.axes.containers[i].patches[0].get_facecolor() )
In other words, I seemed to need an axes attribute in between the axis handle and the containers handle in order for it to be a bit more general.