I am trying to plot a simple chart with markers as "x" (green colour) if datapoint is even and "o" (red color) if datapoint is odd. However the chart is rendered with all markers as "o" except the last which is correctly showing as "x". Pls guide.
import matplotlib.pyplot as plt
a = []
b = []
for i in range( 1, 11 ):
a.append( i )
b.append( i )
if ( i % 2 ) == 0:
plt.plot( a, b, "gx" )
else:
plt.plot( a, b, "ro" )
plt.show()
If you look carefully, the first markers are not red "o"'s, but red circles with a green "x" inside. The for loop you made is equivalent to:
plt.plot([1], [1], "ro")
plt.plot([1, 2], [1, 2], "gx")
plt.plot([1, 2, 3], [1, 2, 3], "ro")
(...)
As a consequence, you will plot 10 different graphics (technically lines.Lines2D objects). The last object you plot, for i=10, is "gx"; it ends up on top of the others.
Here's a corrected version of your algorithm (make one plot per point):
# Not a good way to go about it
import matplotlib.pyplot as plt
# Loop, one plot per iteration
for i in range(1,11):
if i % 2 == 0:
plt.plot(i, i, "gx")
else:
plt.plot(i, i, "ro")
plt.show()
Here's a better algorithm, though:
# use this one instead
import matplotlib.pyplot as plt
# Generate a, b outside the body of the loop
a = list(range(1,11))
b = list(range(1,11))
# Make one plot per condition branch
# See also https://stackoverflow.com/questions/509211/understanding-slice-notation
plt.plot(a[0::2], b[0::2], "gx")
plt.plot(a[1::2], b[1::2], "ro")
plt.show()
Related
I have a df with two columns:
y: different numeric values for the y axis
days: the names of four different days (Monday, Tuesday, Wednesday, Thursday)
I also have a colormap with four different colors that I made myself and it's a ListedColorMap object.
I want to create a bar chart with the four categories (days of the week) in the x axis and their corresponding values in the y axis. At the same time, I want each bar to have a different color using my colormap.
This is the code I used to build my bar chart:
def my_barchart(my_df, my_cmap):
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.bar(my_df['days'], my_df['y'], color=my_cmap)
return fig
However, I get the following error: "object of type 'ListedColormap' has no len()", so it seems that I'm not using my_cmap correctly.
If I remove that from the function and run it, my bar chart looks ok, except that all bars have the same color. So my question is: what is the right way to use a colormap with a bar chart?
The color argument wants either a string or an RGB[A] value (it can be a single colour, or a sequence of colours with one for each data point you are plotting). Colour maps are typically callable with floats in the range [0, 1].
So what you want to do is take the values you want for the colours for each bar, scale them to the range [0, 1], and then call my_cmap with those rescaled values.
So, say for example you wanted the colours to correspond to the y values (heights of the bars), then you should modify your code like this (assumes you have called import numpy as np earlier on):
def my_barchart(my_df, my_cmap):
rescale = lambda y: (y - np.min(y)) / (np.max(y) - np.min(y))
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.bar(my_df['days'], my_df['y'], color=my_cmap(rescale(my_df['y'])))
return fig
Here is a self-contained minimal example of using the color argument with the output from a cmap:
import matplotlib.pyplot as plt
import numpy as np
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
my_cmap = plt.get_cmap("viridis")
rescale = lambda y: (y - np.min(y)) / (np.max(y) - np.min(y))
plt.bar(x, y, color=my_cmap(rescale(y)))
plt.savefig("temp")
Output:
Okay, I found a way to do this without having to scale my values:
def my_barchart(my_df, my_cmap):
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.bar(my_df['days'], my_df['y'], color=my_cmap.colors)
return fig
Simply adding .colors after my_cmap works!
I want to plot trajectories, without connecting the points from boundaries. Attached an image of what i mean.
My code:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
# import polygon as poly
x, y = np.loadtxt('c55.txt', delimiter=' ', unpack=True)
plt.plot(x, y, '.' ,color = 'k' , markersize=0.5)
#for i in range(1, len(x),1):
#if abs(x[i]-x[i+1])>300:
plt.plot(x,y,'-o',color='red',ms=5,label="Window 1")
plt.show()
Your x-values go several times from low to high. plt.plot connects all points in the order they are encountered in the x and y arrays.
The following approach firsts looks for the indices where the x-values start again (so, where the difference of successive x's isn't positive).
These indices are then used to draw the separate curves.
from matplotlib.colors import ListedColormap
import numpy as np
# first create some test data a bit similar to the given ones.
x = np.tile(np.linspace(-3, 3, 20), 4)
y = np.cos(x) + np.repeat(np.linspace(-3, 3, 4), 20)
fig, axs = plt.subplots(ncols=2, figsize=(15, 4))
# plotting the test data without change
axs[0].plot(x, y, '-o')
bounds = np.argwhere(np.diff(x) < 0).squeeze() # find the boundaries
bounds = np.concatenate([[0], bounds + 1, [len(x)]]) # additional boundaries for the first and last point
for b0, b1 in zip(bounds[:-1], bounds[1:]):
axs[1].plot(x[b0:b1], y[b0:b1], '-o') # use '-ro' for only red curves
plt.show()
I have a dataframe and I want to plot the seaborn heatmap:
import seaborn as sns
res = sns.heatmap(df, cmap='flare',xticklabels=1, yticklabels=1,linecolor='white',linewidths=0.5,
cbar=True,mask=df.isnull(),cbar_kws={'shrink': 0.6},vmin=vmin, vmax=vmax)
I have applied a mask for NaN cells. Now, I want to change the color of few cells by a customized color which is not in the colormap, for example blue, to show that those cells belong to another category.
My question is:
Is it possible to apply 2 masks or more with different colors to a seaborn heatmap or manually change the color of a cell to totally another color?
It is unclear how the blue squares are represented. The following solution supposes they are represented as ones in a second matrix. The first heatmap is drawn as before. Then the second heatmap uses a special colormap (in this case with one color, but also a full range is possible), masking out all places where nothing should be drawn.
Note that masks can be combined via the logical or (symbol: |).
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
import seaborn as sns
import pandas as pd
import numpy as np
N = 10
data = np.random.uniform(0, 45, size=(N, N))
for x, y in np.random.randint(0, N, 50).reshape(-1, 2):
data[x, y] = np.nan # fill in some nans at random places
df = pd.DataFrame(data)
up_triang = np.triu(np.ones_like(data)).astype(bool)
ax = sns.heatmap(df, cmap='flare', xticklabels=True, yticklabels=True, square=True,
linecolor='white', linewidths=0.5,
cbar=True, mask=df.isnull() | up_triang, cbar_kws={'shrink': 0.6, 'pad': 0}, vmin=0, vmax=45)
data_special = np.random.randint(0, 5, size=(N, N)) // 4
sns.heatmap(data_special, cmap=ListedColormap(['cornflowerblue']), linecolor='white', linewidths=0.5,
square=True, cbar=False, mask=(data_special != 1) | up_triang, ax=ax)
ax.plot([0, N, 0, 0], [0, N, N, 0], clip_on=False, color='black', lw=2)
ax.tick_params(left=False, bottom=False)
plt.show()
An alternative approach, when there is only one color for the special cells, is to use an "under" color for the colormap, and give these cells negative values. An additional benefit is that the color can be shown in the colorbar. Here is some sample code:
N = 10
data = np.random.uniform(0, 45, size=(N, N))
for x, y in np.random.randint(0, N, 50).reshape(-1, 2):
data[x, y] = np.nan
data_special = np.random.randint(0, 5, size=(N, N)) // 4
data[data_special == 1] = -1
df = pd.DataFrame(data)
up_triang = np.triu(np.ones_like(data)).astype(bool)
cmap = sns.color_palette('mako', as_cmap=True).copy()
cmap.set_under('crimson ')
ax = sns.heatmap(df, cmap=cmap, xticklabels=True, yticklabels=True, square=True,
linecolor='white', linewidths=0.5, cbar=True, mask=df.isnull() | up_triang,
cbar_kws={'shrink': 0.6, 'pad': 0, 'extend': 'min', 'extendrect': True}, vmin=0, vmax=45)
ax.plot([0, N, 0, 0], [0, N, N, 0], clip_on=False, color='black', lw=2)
ax.tick_params(left=False, bottom=False)
plt.show()
I want to plot 2-dimensional scalar data, which I would usually plot using matplotlib.pyplot.imshow or sns.heatmap. Consider this example:
data = [[10, 20, 30], [50, 50, 100], [80, 60, 10]]
fix, ax = plt.subplots()
ax.imshow(data, cmap=plt.cm.YlGn)
Now I additionally would like to have one-dimonsional bar plots at the top and the right side, showing the sum of the values in each column / row - just as sns.jointplot does. However, sns.jointplot seems only to work with categorical data, producing histograms (with kind='hist'), scatterplots or the like - I don't see how to use it if I want to specify the values of the cells directly. Is such a thing possible with seaborn?
The y axis in my plot is going to be days (within a month), the x axis is going to be hours. My data looks like this:
The field Cost Difference is what should make up the shade of the respective field in the plot.
Here is an approach that first creates a dummy jointplot and then uses its axes to add a heatmap and bar plots of the sums.
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
D = 28
H = 24
df = pd.DataFrame({'day': np.repeat(range(1, D + 1), H),
'hour': np.tile(range(H), D),
'Cost Dif.': np.random.uniform(10, 1000, D * H)})
# change the random df to have some rows/columns stand out (debugging, checking)
df.loc[df['hour'] == 10, 'Cost Dif.'] = 150
df.loc[df['hour'] == 12, 'Cost Dif.'] = 250
df.loc[df['day'] == 20, 'Cost Dif.'] = 800
g = sns.jointplot(data=df, x='day', y='hour', kind='hist', bins=(D, H))
g.ax_marg_y.cla()
g.ax_marg_x.cla()
sns.heatmap(data=df['Cost Dif.'].to_numpy().reshape(D, H).T, ax=g.ax_joint, cbar=False, cmap='Blues')
g.ax_marg_y.barh(np.arange(0.5, H), df.groupby(['hour'])['Cost Dif.'].sum().to_numpy(), color='navy')
g.ax_marg_x.bar(np.arange(0.5, D), df.groupby(['day'])['Cost Dif.'].sum().to_numpy(), color='navy')
g.ax_joint.set_xticks(np.arange(0.5, D))
g.ax_joint.set_xticklabels(range(1, D + 1), rotation=0)
g.ax_joint.set_yticks(np.arange(0.5, H))
g.ax_joint.set_yticklabels(range(H), rotation=0)
# remove ticks between heatmao and histograms
g.ax_marg_x.tick_params(axis='x', bottom=False, labelbottom=False)
g.ax_marg_y.tick_params(axis='y', left=False, labelleft=False)
# remove ticks showing the heights of the histograms
g.ax_marg_x.tick_params(axis='y', left=False, labelleft=False)
g.ax_marg_y.tick_params(axis='x', bottom=False, labelbottom=False)
g.fig.set_size_inches(20, 8) # jointplot creates its own figure, the size can only be changed afterwards
# g.fig.subplots_adjust(hspace=0.3) # optionally more space for the tick labels
g.fig.subplots_adjust(hspace=0.05, wspace=0.02) # less spaced needed when there are no tick labels
plt.show()
Here's a really simple line chart.
%matplotlib notebook
import matplotlib.pyplot as plt
lines = plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.setp(lines,marker='D')
plt.ylabel('foo')
plt.xlabel('bar')
plt.show()
If I move my mouse over the chart, I get the x and y values for wherever the pointer is. Is there any way to only get values only when I'm actually over a data point?
I understood you wanted to modify the behavior of the coordinates displayed in the status bar at the bottom right of the plot, is that right?
If so, you can "hijack" the Axes.format_coord() function to make it display whatever you want. You can see an example of this on matplotlib's example gallery.
In your case, something like this seem to do the trick?
my_x = np.array([1, 2, 3, 4])
my_y = np.array([1, 4, 9, 16])
eps = 0.1
def format_coord(x, y):
close_x = np.isclose(my_x, x, atol=eps)
close_y = np.isclose(my_y, y, atol=eps)
if np.any(close_x) and np.any(close_y):
return 'x=%s y=%s' % (ax.format_xdata(my_x[close_x]), ax.format_ydata(my_y[close_y]))
else:
return ''
fig, ax = plt.subplots()
ax.plot(my_x, my_y, 'D-')
ax.set_ylabel('foo')
ax.set_xlabel('bar')
ax.format_coord = format_coord
plt.show()