Seaborn: Create a bar chart with gradient colours - plot

I have a list containing the names of different methods and their performance on a test set which I want to show using a bar chart. Well, in fact I would like to draw their relative improvement/degradation with respect to the baseline model. So, the data looks like:
system_1,+2.5
system_2,-0.8
system_3,+0.24
I've tried the bar chart in seaborn which gives me a simple bar chart with a fixed color. But, what I am looking for a bar chart in which the colours are in the range of red, white, green where the red corresponds to data['score'].min(), white corresponds to 0 and green represents the data['score'].max(). I would like the darkness/brightness of the colours show their distance from 0 meaning that dark red shows the worst system and dark green shows the best performing system and all the performances in the middle being shown by lighter colours.
I've found some solutions to make gradient colours, but they don't do what I expect. Here is my code and the chart that I get.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import sys
import numpy as np
sns.set(style="whitegrid", color_codes=True)
data = pd.read_csv(sys.argv[1])
pal = sns.color_palette("Greens_d", len(data))
colors = [0 if c >=0 else 1 for c in data['performance']]
ax = sns.barplot(x="performance", y="System", data=data, palette=pal)
plt.tight_layout()
plt.show()
As you see, instead of making the color range depending on the value of the datapoints it varies the colors based on the indexes of the data points. Do you have any idea on this?
Thank you very much!

The following approach uses a diverging norm and the red-yellow-green colormap to map the lowest value to the red extreme, zero to yellow and the highest to green.
As the short bars get a very light color, a black edge is added to make every bar clearly visible.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import DivergingNorm
import numpy as np
sns.set(style='whitegrid', color_codes=True)
N = 11
data = pd.DataFrame({'System': [f'System {i}' for i in range(1, N + 1)],
'performance': np.random.uniform(-1.5, 2.5, N)})
norm = DivergingNorm(vmin=data.performance.min(), vcenter=0, vmax=data.performance.max())
colors = [plt.cm.RdYlGn(norm(c)) for c in data['performance']]
ax = sns.barplot(x='performance', y='System', data=data, palette=colors, edgecolor='black')
plt.tight_layout()
plt.show()
Seaborn's diverging_palette can be used to create a color palette given two hue values. A hue of 0 is red, a hue of 150 is green. Default the center is white. You can experiment with saturation s=80 and lightness l=55.
red_green_pal = sns.diverging_palette(0, 150, n=256, as_cmap=True)
colors = [red_green_pal(norm(c)) for c in data['performance']]

Related

Networkx color graph edges based on weight value

I have a dataframe with some columns. I've created a Networkx undirected graph and I want to draw the corresponding network. I need also to change the color of the edges based on the weights of those edges, so I used a LinearColorMap. This is the code:
#my other stuff
cmap = LinearSegmentedColormap.from_list('RwG',['red','white','green'])
nx.draw(G, poss, node_size=1500, node_color='lightgrey', edgelist=edges,edge_color=weights,
width=list(map(lambda number: number * 16 / m, x)), edge_cmap=cmap)
However, I need to normalize my color map so that the white color is centered on a specific value (e.g. -76). The weights are in the [-60,-100] range.
How can I achieve that ?
Visually:
If you pass in a matplotlib colormap to networkx, networkx will normalize your numerical color argument linearly between the minimum and maximum value.
Personally, I think this is a somewhat shortsighted design decision but it is what it is: your non-linear mapping of weights to color is simply not possible.
You can, however, pre-compute the colors and pass those in instead (similarly how you are precomputing edge widths):
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def weight_to_color(w, threshold):
if w < threshold:
return 'green'
elif np.isclose(w , threshold):
return 'white'
else: # x > threshold
return 'red'
weight_matrix = 40 * np.random.rand(10, 10) - 100
g = nx.from_numpy_array(weight_matrix)
weights = [g.edges[edge]['weight'] for edge in g.edges]
nx.draw(g, node_color='lightgray', edgelist=g.edges, edge_color=[weight_to_color(w, -76) for w in weights])
plt.show()

How to apply Savgol filter (Savitzky–Golay filter) to smooth line in line graph in python?

Hi I have this code which works out pixel whiteness from bottom of image to top of image along a vertical line (x=something) on the image. It then plots graph. I want to smooth line using savgol filter in python. I have read online bust struggling.
I would really appreciate if you could help add to my code to smooth line using savgol filter.
Please find my existing code below:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# load image
img = cv2.imread(r'C:\Users\Guest_\Downloads\Screenshot 2022-03-18 154330.png')
# convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# read each column of the image from left to right and save it to a list
cols = []
for i in range(gray.shape[1]):
cols.append(gray[:, i])
# average every 3 columns
avg_cols = []
for i in range(0, len(cols), 1):
avg_cols.append(np.mean(cols[i:i+5], axis=0))
# graph the average of each column (reversed)
plt.plot(avg_cols[60][::-1])
plt.show()
print (avg_cols[60][::-1])

3-dimensional graph LightGraphs/GraphPlot/Julia or Networkx/Python

I am wondering whether it is possible to plot a vertical bar over a 2-dimensional representation of a graph. Say I have a tree and I want to associate with any node a "potential" which can be represented as a vertical bar.
NetworkX can do that using the matplotlib drawing tools because the result is a matplotlib figure on which you can use matplotlib to draw anything else you'd like on top of the networkx drawing of the graph.
nx.draw(G)
mpl.plot([xpt, xpt], [ymin, ymax], '--b')
mpl.show()
This is a minimal example which does what I was looking for (in Python):
import networkx as nx
import random
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
degree = 3
N = 10
g = nx.random_regular_graph(degree, N)
fig = plt.figure(figsize=(10,7))
ax = Axes3D(fig)
for i,j in enumerate(g.edges()):
x = np.array((positions[j[0]][0], positions[j[1]][0]))
y = np.array((positions[j[0]][1], positions[j[1]][1]))
ax.plot(x, y, c='black', alpha=0.5)
for key, value in positions.items():
xi = value[0]
yi = value[1]
# Scatter plot
ax.scatter(xi, yi, c= 'red')
ax.bar3d(xi, yi, 0, 0.01, 0, random.random(), shade=False)
ax.set_axis_off()
It generates this kind of plot, which can be useful to represent additional information on a graph

Plotting triangular of matrix with diagonal at bottom

I would like to plot data which is defined only for each unordered pair of distinct elements in a set. This is naturally represented by a lower-- or upper--triangular matrix, with no values on the diagonal. A correlation matrices is an example.
I might plot it like this
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
mat = np.random.randn(12,12)
# Generate a mask for the upper triangle
mask = np.triu(np.ones_like(mat, dtype=bool))
sns.heatmap(mat, mask=mask, cmap="vlag", square=True, linewidths=.5, cbar=None)
giving
But I would prefer to plot it like this
Or perhaps with the labels showing each once on the hypoteneuse/diagonal would be nicer, rather than repeated on the two sides like I have done.
Is there a natural way to do this with pyplot or seaborn (or R)? I'm sure there's a relatively simple hacky way, but I wonder if there's a package out there that already does something like this. It seems like a natural way to represent symmetric relation data.

Online tool for picking points on the 2D plane and exporting them to coordinates)

I am looking for a specific online tool. At first it displays empty 2D plot (with gridlines from -10 to 10 for example). You can also choose a color. When I select a color and then click on the plot a new point should be drawn on the plot. I can click multiple times so that multiple points are generated on the plot. Then I can change the color and generate more points on the same plot (but with different color). When I'm done I should be able to export the points to list of coordinates and color: [(0, 1, 'blue'), (1, 1, 'green'), (1, 2, 'green')].
Does anyone know such tool? It's purpose is to simply quickly generate 2D dataset with multiple classes.
I wasn't able to find a tool that would exactly meet all your requirements but I think there is a solution that my fulfill some of them.
You can use plotly (https://plot.ly/create/) to plot visualize the points using scatter plot creator.
As for random points you can generate them randomly as well as assign colors to them using some simple python function, like this:
import pandas as pd
import numpy as np
import random
def make_points(minv,maxv,total):
df = pd.DataFrame(np.random.uniform(low=minv, high=maxv, size=(total,2)), columns=list('XY'))
arr=["blue", "green", "purple", "red"]
arr *= total // len(arr)
random.shuffle(arr)
df['color'] = arr
df.to_csv("points")
return df
make_points(-10,10,100)
This for example will create a dataframe with 100 2d points that can get values from -10, 10, and each is randomly assigned one of 4 colors.
Import the csv in the plotly chart creator and you can then manually edit the values if you like.

Resources