matplotlib imshow a matrix of data has white lines, draw a heatmap of a function y=f(x)
Question:
I want to plot a heatmap of a function y = f(x). y is in [0, 1] and x is in [0, 2]. When x <= 1, f(x)=1; and when x > 1, f(x) = y.
First of all, I generate a matrix of data with a grid size of 0.1, which looks fine. But I want to make the color change smoothly, so I decreased the grid size to 0.02 and 0.01. The strange thing is that now the figure has some weird white lines.
I search online and found some solutions (e.g. How to get rid of white lines in confusion matrix?) like plt.grid(None)
, ax.grid(None)
, or plt.rcParams["axes.grid"] = False
. I tried all of them, but none of them works for me. Here is my code for drawing the map when gridsize = 0.01.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
plt.rcParams["axes.grid"] = False
if __name__ == '__main__':
heat_results = np.empty((101, 101))
heat_results[:, :] = np.nan
for x in np.arange(0.0, 1.01, 0.01):
for y in np.arange(0.0, 2.02, 0.02):
ind_x = int(x*100)
ind_y = int(y*50)
if y <= 1.0: heat_results[ind_x][ind_y] = 1.0
else: heat_results[ind_x][ind_y] = x
minv = np.nanmin(heat_results)
maxv = np.nanmax(heat_results)
# print('minv, maxv:')
# print(minv)
# print(maxv)
colors = ["lime", "saddlebrown"]
cmap = LinearSegmentedColormap.from_list("mycmap", colors)
heat_results = (heat_results - minv) / (maxv - minv)
fig, ax = plt.subplots()
ax.grid(False)
plt.grid(None)
plt.grid(False)
im = ax.imshow(heat_results, origin='lower',cmap=cmap)
ax.grid(False)
plt.grid(None)
plt.grid(False)
cbar = ax.figure.colorbar(im, ax=ax)
plt.xticks(np.arange(0, 101, 10), np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0]))
plt.yticks(np.arange(0, 101, 10), np.array([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]))
plt.xlabel('t')
plt.ylabel('x')
plt.savefig('fig/heat.pdf')
plt.close()
I also attach my three figures here, you can see how the white line appears when I decrease the step size (smooth the color change).
Any ideas about why these white lines appear when I decrease the grid size? Or any ideas about how to draw the heatmap of the function I described before, besides generating a matrix of data? Basically, I want a figure that looks like the first figure but with a smooth color change (like the color changes in the color bar), and the separation of the left part and the right part of the figure happens exactly at 1.0 (figure 1 does not lie at 1.0 because the grid size is too large).
Answers:
Your white lines aren’t related to the grid. These are NaN
values that are still in the heat_results
. Due to floating point approximation errors, int(x*100)
sometimes gets a lower value. In your case, you could use int(round(x*100))
, but it would be much better to fully use numpy functions. These are much faster (and also easier to read once you get used to them).
The recommended way to automatically set tick labels for a heatmap uses extent=[x0,x1,y0,y1]
.
Filling arrays can be simplified using numpy’s broadcasting. For an if
test, numpy works with np.where
.
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
x = np.linspace(0, 1, 101)
y = np.linspace(0, 2, 101)
ys, xs = np.meshgrid(y, x)
heat_results = np.where(ys <= 1, 1.0, xs)
colors = ["lime", "saddlebrown"]
cmap = LinearSegmentedColormap.from_list("mycmap", colors)
fig, ax = plt.subplots()
im = ax.imshow(heat_results, origin='lower', cmap=cmap, extent=[y.min(), y.max(), x.min(), x.max()], aspect='auto')
cbar = ax.figure.colorbar(im, ax=ax)
plt.xlabel('t')
plt.ylabel('x')
plt.show()
I want to plot a heatmap of a function y = f(x). y is in [0, 1] and x is in [0, 2]. When x <= 1, f(x)=1; and when x > 1, f(x) = y.
First of all, I generate a matrix of data with a grid size of 0.1, which looks fine. But I want to make the color change smoothly, so I decreased the grid size to 0.02 and 0.01. The strange thing is that now the figure has some weird white lines.
I search online and found some solutions (e.g. How to get rid of white lines in confusion matrix?) like plt.grid(None)
, ax.grid(None)
, or plt.rcParams["axes.grid"] = False
. I tried all of them, but none of them works for me. Here is my code for drawing the map when gridsize = 0.01.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
plt.rcParams["axes.grid"] = False
if __name__ == '__main__':
heat_results = np.empty((101, 101))
heat_results[:, :] = np.nan
for x in np.arange(0.0, 1.01, 0.01):
for y in np.arange(0.0, 2.02, 0.02):
ind_x = int(x*100)
ind_y = int(y*50)
if y <= 1.0: heat_results[ind_x][ind_y] = 1.0
else: heat_results[ind_x][ind_y] = x
minv = np.nanmin(heat_results)
maxv = np.nanmax(heat_results)
# print('minv, maxv:')
# print(minv)
# print(maxv)
colors = ["lime", "saddlebrown"]
cmap = LinearSegmentedColormap.from_list("mycmap", colors)
heat_results = (heat_results - minv) / (maxv - minv)
fig, ax = plt.subplots()
ax.grid(False)
plt.grid(None)
plt.grid(False)
im = ax.imshow(heat_results, origin='lower',cmap=cmap)
ax.grid(False)
plt.grid(None)
plt.grid(False)
cbar = ax.figure.colorbar(im, ax=ax)
plt.xticks(np.arange(0, 101, 10), np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0]))
plt.yticks(np.arange(0, 101, 10), np.array([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]))
plt.xlabel('t')
plt.ylabel('x')
plt.savefig('fig/heat.pdf')
plt.close()
I also attach my three figures here, you can see how the white line appears when I decrease the step size (smooth the color change).
Any ideas about why these white lines appear when I decrease the grid size? Or any ideas about how to draw the heatmap of the function I described before, besides generating a matrix of data? Basically, I want a figure that looks like the first figure but with a smooth color change (like the color changes in the color bar), and the separation of the left part and the right part of the figure happens exactly at 1.0 (figure 1 does not lie at 1.0 because the grid size is too large).
Your white lines aren’t related to the grid. These are NaN
values that are still in the heat_results
. Due to floating point approximation errors, int(x*100)
sometimes gets a lower value. In your case, you could use int(round(x*100))
, but it would be much better to fully use numpy functions. These are much faster (and also easier to read once you get used to them).
The recommended way to automatically set tick labels for a heatmap uses extent=[x0,x1,y0,y1]
.
Filling arrays can be simplified using numpy’s broadcasting. For an if
test, numpy works with np.where
.
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
x = np.linspace(0, 1, 101)
y = np.linspace(0, 2, 101)
ys, xs = np.meshgrid(y, x)
heat_results = np.where(ys <= 1, 1.0, xs)
colors = ["lime", "saddlebrown"]
cmap = LinearSegmentedColormap.from_list("mycmap", colors)
fig, ax = plt.subplots()
im = ax.imshow(heat_results, origin='lower', cmap=cmap, extent=[y.min(), y.max(), x.min(), x.max()], aspect='auto')
cbar = ax.figure.colorbar(im, ax=ax)
plt.xlabel('t')
plt.ylabel('x')
plt.show()