The objective is to modify the xticklabel upon plotting pcolormesh and scatter.
However, I am having difficulties accessing the existing xtick labels.
Simply
ax = plt.axes()
labels_x = [item.get_text() for item in ax.get_xticklabels()]
which produced:
['', '', '', '', '', '']
or
fig.canvas.draw()
xticks = ax.get_xticklabels()
which produced:
['', '', '', '', '', '']
does not return the corresponding label.
May I know how to properly access axis tick labels for a plt cases.
For readability, I split the code into two section.
The first section to generate the data used for plotting
Second section deal the plotting
Section 1: Generate data used for plotting
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import math
np.random.seed(0)
increment=120
max_val=172800
aran=np.arange(0,max_val,increment).astype(int)
arr=np.concatenate((aran.reshape(-1,1), np.random.random((aran.shape[0],4))), axis=1)
df=pd.DataFrame(arr,columns=[('lapse',''),('a','i'),('a','j'),('b','k'),('c','')])
ridx=df.index[df[('lapse','')] == 3600].tolist()[0]+1 # minus 1 so to allow 3600 start at new row
df[('event','')]=0
df.loc[[1,2,3,10,20,30],[('event','')]]=1
arr=df[[('a','i'),('event','')]].to_numpy()
col_len=ridx
v=arr[:,0].view()
nrow_size=math.ceil(v.shape[0]/col_len)
X=np.pad(arr[:,0].astype(float), (0, nrow_size*col_len - arr[:,0].size),
mode='constant', constant_values=np.nan).reshape(nrow_size,col_len)
mask_append_val=0 # This value must equal to 1 for masking
arrshape=np.pad(arr[:,1].astype(float), (0, nrow_size*col_len - arr[:,1].size),
mode='constant', constant_values=mask_append_val).reshape(nrow_size,col_len)
Section 2 Plotting
fig = plt.figure(figsize=(8,6))
plt.pcolormesh(X,cmap="plasma")
x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)
plt.scatter(ys[:,u[:,1]].ravel()+.5,xs[u[:,0]].ravel()+0.5,marker='*', color='r', s=55)
plt.gca().invert_yaxis()
xlabels_to_use_this=df.loc[:30,[('lapse','')]].values.tolist()
# ax = plt.axes()
# labels_x = [item.get_text() for item in ax.get_xticklabels()]
# labels_y = [item.get_text() for item in ax.get_yticklabels()]
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title("Plot 2D array")
plt.colorbar()
plt.tight_layout()
plt.show()
Expected output
This is how the plot could be generated using matplotlib's pcolormesh and scatter:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import pandas as pd
import numpy as np
np.random.seed(0)
increment = 120
max_val = 172800
aran = np.arange(0, max_val, increment).astype(int)
arr_df = np.concatenate((aran.reshape(-1, 1), np.random.random((aran.shape[0], 4))), axis=1)
df = pd.DataFrame(arr_df, columns=[('lapse', ''), ('a', 'i'), ('a', 'j'), ('b', 'k'), ('c', '')])
df[('event', '')] = 0
df.loc[[1, 2, 3, 10, 20, 30], [('event', '')]] = 1
col_len_lapse = 3600
col_len = df[df[('lapse', '')] == col_len_lapse].index[0]
nrow_size = int(np.ceil(v.shape[0] / col_len))
a_i_values = df[('a', 'i')].values
a_i_values_meshed = np.pad(a_i_values.astype(float), (0, nrow_size * col_len - len(a_i_values)),
mode='constant', constant_values=np.nan).reshape(nrow_size, col_len)
fig, ax = plt.subplots(figsize=(8, 6))
# the x_values indicate the mesh borders, subtract one half so the ticks can be at the centers
x_values = df[('lapse', '')][:col_len + 1].values - increment / 2
# divide lapses for y by col_len_lapse to get hours
y_values = df[('lapse', '')][::col_len].values / col_len_lapse - 0.5
y_values = np.append(y_values, 2 * y_values[-1] - y_values[-2]) # add the bottommost border (linear extension)
mesh = ax.pcolormesh(x_values, y_values, a_i_values_meshed, cmap="plasma")
event_lapses = df[('lapse', '')][df[('event', '')] == 1]
ax.scatter(event_lapses % col_len_lapse,
np.floor(event_lapses / col_len_lapse),
marker='*', color='red', edgecolor='white', s=55)
ax.xaxis.set_major_locator(MultipleLocator(increment * 5))
ax.yaxis.set_major_locator(MultipleLocator(5))
ax.invert_yaxis()
ax.set_xlabel('X-axis (s)')
ax.set_ylabel('Y-axis (hours)')
ax.set_title("Plot 2D array")
plt.colorbar(mesh)
plt.tight_layout() # fit the labels nicely into the plot
plt.show()
With Seaborn things can be simplified, adding new columns for hours and seconds, and using pandas' pivot (which automatically fills unavailable data with NaNs). Adding xtick_labels=5 sets the labels every 5 positions. (The star for lapse=3600 is at 1 hour, 0 seconds).
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# df created as before
df['hours'] = (df[('lapse', '')].astype(int) // 3600)
df['seconds'] = (df[('lapse', '')].astype(int) % 3600)
df_heatmap = df.pivot(index='hours', columns='seconds', values=('a', 'i'))
df_heatmap_markers = df.pivot(index='hours', columns='seconds', values=('event', '')).replace(
{0: '', 1: '★', np.nan: ''})
fig, ax = plt.subplots(figsize=(8, 6))
sns.heatmap(df_heatmap, xticklabels=5, yticklabels=5,
annot=df_heatmap_markers, fmt='s', annot_kws={'color': 'lime'}, ax=ax)
ax.tick_params(rotation=0)
plt.tight_layout()
plt.show()
Instead of a 'seconds' column, a 'minutes' column also might be interesting.
Here is an attempt to add time information as suggested in the comments:
from matplotlib import patheffects # to add some outline effect
# df prepared as the other seaborn example
fig, ax = plt.subplots(figsize=(8, 6))
path_effect = patheffects.withStroke(linewidth=2, foreground='yellow')
sns.heatmap(df_heatmap, xticklabels=5, yticklabels=5,
annot=df_heatmap_markers, fmt='s',
annot_kws={'color': 'red', 'path_effects': [path_effect]},
cbar=True, cbar_kws={'pad': 0.16}, ax=ax)
ax.tick_params(rotation=0)
ax2 = ax.twinx()
ax2.set_ylim(ax.get_ylim())
yticks = ax.get_yticks()
ax2.set_yticks(yticks)
ax2.set_yticklabels([str(pd.to_datetime('2019-01-15 7:00:00') + pd.to_timedelta(h, unit='h')).replace(' ', '\n')
for h in yticks])
I end up using Seaborn to address this issue.
Specifically, the following lines able to easily tweak the xticklabel
fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]
i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]
The full code for plotting is as below
import seaborn as sns
fig, ax = plt.subplots()
sns.heatmap(X,ax=ax)
x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)
g=sns.scatterplot(ys[:,u[:,1]].ravel()+.5,xs[u[:,0]].ravel()+0.5,marker='*', color='r', s=55)
fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]
i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]
ax.set_xticklabels(newlabel)
ax.set_xticklabels(ax.get_xticklabels(),rotation = 90)
for ind, label in enumerate(g.get_xticklabels()):
if ind % 2 == 0: # every 10th label is kept
label.set_visible(True)
else:
label.set_visible(False)
for ind, label in enumerate(g.get_yticklabels()):
if ind % 4 == 0: # every 10th label is kept
label.set_visible(True)
else:
label.set_visible(False)
plt.xlabel('Elapsed (s)')
plt.ylabel('Hour (h)')
plt.title("Rastar Plot")
plt.tight_layout()
plt.show()
Related
I would like to add image annotations to a boxplot, akin to what they did with the bar chart in this post:
How can I add images to bars in axes (matplotlib)
My dataframe looks like this:
import pandas as pd
import numpy as np
names = ['PersonA', 'PersonB', 'PersonC', 'PersonD','PersonE','PersonF']
regions = ['NorthEast','NorthWest','SouthEast','SouthWest']
dates = pd.date_range(start = '2021-05-28', end = '2021-08-23', freq = 'D')
df = pd.DataFrame({'runtime': np.repeat(dates, len(names))})
df['name'] = len(dates)*names
df['A'] = 40 + 20*np.random.random(len(df))
df['B'] = .1 * np.random.random(len(df))
df['C'] = 1 +.5 * np.random.random(len(df))
df['region'] = np.resize(regions,len(df))
I tried to use the AnnotationBbox method which worked great for my time-series, but I'm not entirely sure if it can be applied here.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from matplotlib.cbook import get_sample_data
fig, ax = plt.subplots(
df.boxplot(column='A', by=['name'],ax=ax,showmeans=True, fontsize=8, grid=False)
for name in names:
rslt_df = df[df['name']==name]
val = rslt_df['A'].values[0]
xy = (0, val)
fn = get_sample_data(f"{name}.png", asfileobj=False)
arr_img = plt.imread(fn, format='png')
imagebox = OffsetImage(arr_img, zoom=0.125)
imagebox.image.axes = ax
ab = AnnotationBbox(imagebox, xy,xybox=(15.,0),xycoords='data',boxcoords="offset points",pad=0,frameon=False)
ax.add_artist(ab)
The code in the OP if very similar to Add image annotations to bar plots axis tick labels, but needs to be modified because boxplots are slightly different the barplots.
The main issue was xy didn't have the correct values.
The xy and xybox parameters can be adjusted to place the images anywhere.
By default, boxplot positions the ticks at range(1, n+1), as explained in this answer
Reset the tick positions with a 0 index: positions=range(len(names))
df was created with names = ['PersonA', 'PersonB', 'PersonC'] since only 3 images were provided.
ax = df.boxplot(column='A', by=['name'], showmeans=True, fontsize=8, grid=False, positions=range(len(names)))
ax.set(xlabel=None, title=None)
# move the xtick labels
ax.set_xticks(range(len(names)))
ax.set_xticklabels(countries)
ax.tick_params(axis='x', which='major', pad=30)
# use the ytick values to locate the image
y = ax.get_yticks()[1]
for i, (name, data) in enumerate(df.groupby('name')):
xy = (i, y)
fn = f"data/so_data/2021-08-28/{name}.png" # path to file
arr_img = plt.imread(fn, format='png')
imagebox = OffsetImage(arr_img, zoom=0.125)
imagebox.image.axes = ax
ab = AnnotationBbox(imagebox, xy, xybox=(0, -30), xycoords='data', boxcoords="offset points", pad=0, frameon=False)
ax.add_artist(ab)
I noticed that the y-ticks don't always position themselves in a friendly manner, so I set a static Y-value (the x-axis). Creating a transform xycoords, allows placement directly below the x-axis, no matter the y-tick scale.
# BOX GRAPH PLOT
fig, ax = plt.subplots(facecolor='darkslategrey')
plt.style.use('dark_background')
ax = df.boxplot(column=str(c), by=['name'],ax=ax,showmeans=True, fontsize=8,grid=False,positions=range(len(top)))
ax.set(xlabel=None, title=None)
# move the xtick labels
ax.set_xticks(range(len(top)))
ax.tick_params(axis='x', which='major', pad=20)
# use the ytick values to locate the image
y = ax.get_xticks()[0]
for i, (name, data) in enumerate(df.groupby('name')):
xy = (i, y)
fn = f"{imgsrc}/{name}.png" # path to file
arr_img = plt.imread(fn, format='png')
imagebox = OffsetImage(arr_img, zoom=0.125)
imagebox.image.axes = ax
trans = ax.get_xaxis_transform()
ab = AnnotationBbox(imagebox, xy, xybox=(0, -15), xycoords=trans,boxcoords="offset points", pad=0, frameon=False)
ax.add_artist(ab)
plt.show()
I'm trying to remake an existing animated line graph I made where each line has a uniquely scaled y-axis - one on the left, one on the right. The graph is comparing the value of two cryptocurrencies that have vastly different sizes (eth/btc), which is why I need multiple scales to actually see changes.
My data has been formatted in a pd df (numbers here are random):
Date ETH Price BTC Price
0 2020-10-30 00:00:00 0.155705 1331.878496
1 2020-10-31 00:00:00 0.260152 1337.174272
.. ... ... ...
290 2021-08-15 16:42:09 0.141994 2846.719819
[291 rows x 3 columns]
And code is roughly:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani
color = ['cyan', 'orange', 'red']
fig = plt.figure()
plt.xticks(rotation=45, ha="right", rotation_mode="anchor")
plt.subplots_adjust(bottom = 0.2, top = 0.9)
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')
def buildChart(i=int):
df1 = df.set_index('Date', drop=True)
plt.legend(["ETH Price", "BTC Price"])
p = plt.plot(df1[:i].index, df1[:i].values)
for i in range(0,2):
p[i].set_color(color[i])
animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()
Resulting Animation
I tried to create a second axis with a twin x to the first axis.
color = ['cyan', 'orange', 'blue']
fig, ax1 = plt.subplots() #Changes over here
plt.xticks(rotation=45, ha="right", rotation_mode="anchor")
plt.subplots_adjust(bottom = 0.2, top = 0.9)
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')
def buildChart(i=int):
df1 = df.set_index('Date', drop=True)
plt.legend(["ETH Price", "Bitcoin Price"])
data1 = df1.iloc[:i, 0:1] # Changes over here
# ------------- More Changes Start
ax2 = ax1.twinx()
ax2.set_ylabel('Cost of Coin (USD)')
data2 = df1.iloc[:i, 1:2]
ax2.plot(df1[:i].index, data2)
ax2.tick_params(axis='y')
# -------------- More Changes End
p = plt.plot(df1[:i].index, data1)
for i in range(0,1):
p[i].set_color(color[i])
import matplotlib.animation as ani
animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()
Resulting Animation After Changes
Current issues:
X-Axis start at ~1999 rather than late 2020
---- Causes all changes on the y-axis to be a nearly vertical line
Left Y-Axis label is on a scale of 0-1?
Right y-axis labels are recurring, overlapping, moving.
I believe my approach to making a second scale must have been wrong to get this many errors, but this seems like the way to do it.
I re-structured your code in order to easily set up a secondary axis animation.
Here the code of the animation with a single y axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))
def update(i):
ax.cla()
ax.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
ax.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')
ax.legend(frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))
ax.set_ylim(0.9*min(df['ETH'].min(), df['BTC'].min()), 1.1*max(df['ETH'].max(), df['BTC'].max()))
ax.tick_params(axis = 'x', which = 'both', top = False)
ax.tick_params(axis = 'y', which = 'both', right = False)
plt.setp(ax.xaxis.get_majorticklabels(), rotation = 45)
ax.set_xlabel('Date')
ax.set_ylabel('ETH Coin Value (USD)')
plt.tight_layout()
fig, ax = plt.subplots(figsize = (6, 4))
ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)
plt.show()
Starting from the code above, you should twin the axis out of the update function: if you keep ax.twinx() inside the function, this operation will be repeated in each iteration and you will get a new axis each time.
Below the code for an animation with a secondary axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))
def update(i):
ax1.cla()
ax2.cla()
line1 = ax1.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
line2 = ax2.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')
lines = line1 + line2
labels = [line.get_label() for line in lines]
ax1.legend(lines, labels, frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))
ax1.set_ylim(0.9*df['ETH'].min(), 1.1*df['ETH'].max())
ax2.set_ylim(0.9*df['BTC'].min(), 1.1*df['BTC'].max())
ax1.tick_params(axis = 'x', which = 'both', top = False)
ax1.tick_params(axis = 'y', which = 'both', right = False, colors = 'red')
ax2.tick_params(axis = 'y', which = 'both', right = True, labelright = True, left = False, labelleft = False, colors = 'blue')
plt.setp(ax1.xaxis.get_majorticklabels(), rotation = 45)
ax1.set_xlabel('Date')
ax1.set_ylabel('ETH Coin Value (USD)')
ax2.set_ylabel('BTC Coin Value (USD)')
ax1.yaxis.label.set_color('red')
ax2.yaxis.label.set_color('blue')
ax2.spines['left'].set_color('red')
ax2.spines['right'].set_color('blue')
plt.tight_layout()
fig, ax1 = plt.subplots(figsize = (6, 4))
ax2 = ax1.twinx()
ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)
plt.show()
I have a dataframe with 1000 simulations of a portfolio's returns. I am able to graph the simulations and do the respective histogram separately, but I have absolutely no idea how to merge them in order to resemble the following image:
please take this example of data in order to facilitate answers:
import numpy as np
import pandas as pd
def simulate_panel(T, N):
"""" This function simulates return paths"""
dates = pd.date_range("20210218", periods=T, freq='D')
columns = []
for i in range(N):
columns.append(str(i+1))
return pd.DataFrame(np.random.normal(0, 0.01, size=(T, N)), index=dates,
columns=columns)
df=(1+simulate_panel(1000,1000)).cumprod()
df.plot(figsize=(8,6),title=('Bootstrap'), legend=False)
Thank you very much in advance.
To color the curves via their last value, they can be drawn one-by-one. With a colormap and a norm, the value can be converted to the appropriate color. Using some transparency (alpha), the most visited positions will be colored stronger.
In a second subplot, a vertical histogram can be drawn, with the bars colored similarly.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def simulate_panel(T, N):
"""" This function simulates return paths"""
dates = pd.date_range("20210218", periods=T, freq='D')
columns = [(str(i + 1)) for i in range(N)]
return pd.DataFrame(np.random.normal(0, 0.01, size=(T, N)), index=dates, columns=columns)
df = (1 + simulate_panel(1000, 1000)).cumprod()
fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True, figsize=(12, 4),
gridspec_kw={'width_ratios': [5, 1], 'wspace': 0})
data = df.to_numpy().T
cmap = plt.cm.get_cmap('turbo')
norm = plt.Normalize(min(data[:, -1]), max(data[:, -1]))
for row in data:
ax1.plot(df.index, row, c=cmap(norm(row[-1])), alpha=0.1)
ax1.margins(x=0)
_, bin_edges, bars = ax2.hist(data[:, -1], bins=20, orientation='horizontal')
for x0, x1, bar in zip(bin_edges[:-1], bin_edges[1:], bars):
bar.set_color(cmap(norm((x0 + x1) / 2)))
ax2.tick_params(left=False)
plt.tight_layout()
plt.show()
You can use GridSpec to set up axes for line chart and the histogram next to each other:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
# layout
fig = plt.figure()
gs = fig.add_gridspec(1, 2, wspace=0, width_ratios=[9, 1])
ax = gs.subplots(sharey=True)
# line chart
z = df.iloc[-1]
df.plot(figsize=(8,6), title=('Bootstrap'), legend=False, ax=ax[0],
color=cm.RdYlBu_r((z - z.min()) / (z.max() - z.min())))
# histogram
n_bins = 20
cnt, bins, patches = ax[1].hist(
z, np.linspace(z.min(), z.max(), n_bins),
ec='k', orientation='horizontal')
colors = cm.RdYlBu_r((bins - z.min()) / (z.max() - z.min()))
for i, p in enumerate(patches):
p.set_color(colors[i])
I have the following code that displays the numerical values of a matrix in a matplotlib.table object:
fig = plt.figure(figsize=(20,11))
plt.title('Correlation Matrix')
ticks = np.array(['$F_{sum}$','$F_{dif}$','$x_{sum}$','$x_{dif}$','$y_{sum}$','$y_{dif}$','$HLR_a$','$e1_a$','$e2_a$',
'$HLR_b$','$e1_b$','$e2_b$'])
ticks = ticks[::-1]
ticks = ticks.tolist()
plt.xticks([0.5,1.2,2.1,3.0,3.9,4.8,5.7,6.6,7.5,8.4,9.3,10],ticks,fontsize=15)
plt.yticks([0.5,1.2,2.1,3.0,3.9,4.8,5.7,6.6,7.5,8.4,9.3,10],['$F_{sum}$','$F_{dif}$','$x_{sum}$','$x_{dif}$','$y_{sum}$','$y_{dif}$','$HLR_a$','$e1_a$','$e2_a$',
'$HLR_b$','$e1_b$','$e2_b$'],fontsize=15)
round_mat = np.round(correlation_mat,2)
table = plt.table(cellText=round_mat,loc='center',colWidths=np.ones(correlation_mat.shape[0])/correlation_mat.shape[0],cellLoc='center',bbox=[0,0,1,1])
table.set_fontsize(25)
plt.show()
with the following output:
I want the x-axis and the y-axis ticks to be centered for each rectangle. Here, it seems that the first few ticks are correct and then the rest spread out. I would like them all equally spaced with the tick at the center. I am not sure what to do for this.
One way to do this is to use the row and column labels for the table. By default, they'll have a background and border, which is a touch clunky to turn off:
import numpy as np
import matplotlib.pyplot as plt
# Generate some data...
data = np.random.random((12, 10))
correlation_mat = np.cov(data)
correlation_mat /= np.diag(correlation_mat)
fig, ax = plt.subplots(figsize=(20,11))
ax.set_title('Correlation Matrix')
ticks = ['$F_{sum}$', '$F_{dif}$', '$x_{sum}$', '$x_{dif}$', '$y_{sum}$',
'$y_{dif}$', '$HLR_a$', '$e1_a$', '$e2_a$', '$HLR_b$', '$e1_b$',
'$e2_b$'][::-1]
round_mat = np.round(correlation_mat, 2)
table = ax.table(cellText=round_mat, cellLoc='center', bbox=[0, 0, 1, 1],
rowLabels=ticks, colLabels=ticks)
table.set_fontsize(25)
ax.axis('off')
for key, cell in table.get_celld().iteritems():
if key[0] == 0 or key[1] == -1:
cell.set(facecolor='none', edgecolor='none')
if key[1] == -1:
cell._loc = 'right'
elif key[0] == 0:
cell._loc = 'center'
plt.show()
However, it's sometimes easier to skip using a table for this altogether:
import numpy as np
import matplotlib.pyplot as plt
# Generate some data...
data = np.random.random((12, 10))
correlation_mat = np.cov(data)
correlation_mat /= np.diag(correlation_mat)
num = data.shape[0]
fig, ax = plt.subplots(figsize=(20,11))
ticks = ['$F_{sum}$', '$F_{dif}$', '$x_{sum}$', '$x_{dif}$', '$y_{sum}$',
'$y_{dif}$', '$HLR_a$', '$e1_a$', '$e2_a$', '$HLR_b$', '$e1_b$',
'$e2_b$']
ticks = ticks[::-1]
ax.matshow(correlation_mat, aspect='auto', cmap='cool')
ax.set(title='Correlation Matrix', xticks=range(num), xticklabels=ticks,
yticks=range(num), yticklabels=ticks)
ax.tick_params(labelsize=25)
for (i, j), val in np.ndenumerate(correlation_mat):
ax.annotate('{:0.2f}'.format(val), (j,i), ha='center', va='center', size=25)
plt.show()
I am trying to create a 10x10 grid using either imshow or matshow in Matplotlib. The function below takes a numpy array as input, and plots the grid. However, I'd like to have values from the array also displayed inside the cells defined by the grid. So far I could not find a proper way to do it. I can use plt.text to place things over the grid, but this requires coordinates of each cell, totally inconvenient. Is there a better way to do what I am trying to accomplish?
Thanks!
NOTE: The code below does not take the values from the array yet, I was just playing with plt.text.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
board = np.zeros((10, 10))
def visBoard(board):
cmap = colors.ListedColormap(['white', 'red'])
bounds=[0,0.5,1]
norm = colors.BoundaryNorm(bounds, cmap.N)
plt.figure(figsize=(4,4))
plt.matshow(board, cmap=cmap, norm=norm, interpolation='none', vmin=0, vmax=1)
plt.xticks(np.arange(0.5,10.5), [])
plt.yticks(np.arange(0.5,10.5), [])
plt.text(-0.1, 0.2, 'x')
plt.text(0.9, 0.2, 'o')
plt.text(1.9, 0.2, 'x')
plt.grid()
visBoard(board)
Output:
Can you do something like:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
min_val, max_val = 0, 10
ind_array = np.arange(min_val + 0.5, max_val + 0.5, 1.0)
x, y = np.meshgrid(ind_array, ind_array)
for i, (x_val, y_val) in enumerate(zip(x.flatten(), y.flatten())):
c = 'x' if i%2 else 'o'
ax.text(x_val, y_val, c, va='center', ha='center')
#alternatively, you could do something like
#for x_val, y_val in zip(x.flatten(), y.flatten()):
# c = 'x' if (x_val + y_val)%2 else 'o'
ax.set_xlim(min_val, max_val)
ax.set_ylim(min_val, max_val)
ax.set_xticks(np.arange(max_val))
ax.set_yticks(np.arange(max_val))
ax.grid()
Edit:
Here is an updated example with an imshow background.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
min_val, max_val, diff = 0., 10., 1.
#imshow portion
N_points = (max_val - min_val) / diff
imshow_data = np.random.rand(N_points, N_points)
ax.imshow(imshow_data, interpolation='nearest')
#text portion
ind_array = np.arange(min_val, max_val, diff)
x, y = np.meshgrid(ind_array, ind_array)
for x_val, y_val in zip(x.flatten(), y.flatten()):
c = 'x' if (x_val + y_val)%2 else 'o'
ax.text(x_val, y_val, c, va='center', ha='center')
#set tick marks for grid
ax.set_xticks(np.arange(min_val-diff/2, max_val-diff/2))
ax.set_yticks(np.arange(min_val-diff/2, max_val-diff/2))
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_xlim(min_val-diff/2, max_val-diff/2)
ax.set_ylim(min_val-diff/2, max_val-diff/2)
ax.grid()
plt.show()
For your graph you should should try with pyplot.table:
import matplotlib.pyplot as plt
import numpy as np
board = np.zeros((10, 10))
board[0,0] = 1
board[0,1] = -1
board[0,2] = 1
def visBoard(board):
data = np.empty(board.shape,dtype=np.str)
data[:,:] = ' '
data[board==1.0] = 'X'
data[board==-1.0] = 'O'
plt.axis('off')
size = np.ones(board.shape[0])/board.shape[0]
plt.table(cellText=data,loc='center',colWidths=size,cellLoc='center',bbox=[0,0,1,1])
plt.show()
visBoard(board)
Some elaboration on the code of #wflynny making it into a function that takes any matrix no matter what size and plots its values.
import numpy as np
import matplotlib.pyplot as plt
cols = np.random.randint(low=1,high=30)
rows = np.random.randint(low=1,high=30)
X = np.random.rand(rows,cols)
def plotMat(X):
fig, ax = plt.subplots()
#imshow portion
ax.imshow(X, interpolation='nearest')
#text portion
diff = 1.
min_val = 0.
rows = X.shape[0]
cols = X.shape[1]
col_array = np.arange(min_val, cols, diff)
row_array = np.arange(min_val, rows, diff)
x, y = np.meshgrid(col_array, row_array)
for col_val, row_val in zip(x.flatten(), y.flatten()):
c = '+' if X[row_val.astype(int),col_val.astype(int)] < 0.5 else '-'
ax.text(col_val, row_val, c, va='center', ha='center')
#set tick marks for grid
ax.set_xticks(np.arange(min_val-diff/2, cols-diff/2))
ax.set_yticks(np.arange(min_val-diff/2, rows-diff/2))
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_xlim(min_val-diff/2, cols-diff/2)
ax.set_ylim(min_val-diff/2, rows-diff/2)
ax.grid()
plt.show()
plotMat(X)