Reputation: 115
Im trying to achieve this ideal graphical formatting of the plot:
I successfully generated the graph, also somehow (plt.pcolor
) overwrited the default colors of plot (something that maybe is overkill, but I cannot find other option), but I'm struggling that:
Here is functional code to generate the plot (on the middle picture):
import numpy as np
import pandas as pd
from datetime import datetime as dt
from datetime import timedelta
import matplotlib
import matplotlib.pyplot as plt
from calendar import monthrange
import matplotlib.colors as mcolors
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
fig, ax = plt.subplots()
im = ax.imshow(harvest)
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
#Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
cmap, norm = mcolors.from_levels_and_colors([0, 1, 4], ['grey', 'green'])
plt.pcolor(harvest, cmap=cmap, norm=norm, snap=True)
ax.set_xticks(np.arange(harvest.shape[1]+1)-.5, minor=True)
ax.set_yticks(np.arange(harvest.shape[0]+1)-.5, minor=True)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
plt.show()
plt.close()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
I tried to look in matplotlib but, I'm not more wise than before.
Upvotes: 2
Views: 456
Reputation: 115
This is the result graph made from both of your suggestion!
What I'm struggling is the fine tune the colormap. From all colormaps I chose this one, but the lower values are just too bright. Is there any way how to trim the colormap so it will not start from that yellow but it will start in range I draw (red square)?
Upvotes: 0
Reputation: 80509
Some remarks:
ax.tick_params(which='both', length=0)
. Note that you can call ax.tick_params
many times with different parameters. The tick labels will automatically be set closer to the plot. Default it only operates on the major ticks, so which='both'
makes it also operate on the minor ticks.imshow
has borders at the halves, which places the integer positions nicely in the center of each cell.pcolor
(and pcolormesh
) suppose a grid of positions (default at the integers), so it doesn't align well with imshow
.0.5
works in this case.vmin
and vmax
tell which number corresponds to the lowest and which to the highest color of the colormap.over color
can be set for all values higher than vmax
. This value can be displayed in the colorbar via extend='max'
. (Default, just the highest color is used for all values higher than vmax
, and similarly the lowest color for the values below vmin
). Note that if you use a standard colormap and want to use set_over
, you need to make a copy of the colormap (because set_over
changes the colormap in place and you would want other plots not to be affected).import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.ticker import MultipleLocator
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
fig, ax = plt.subplots()
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
ax.tick_params(which='both', length=0) # hide tick marks
ax.xaxis.set_minor_locator(MultipleLocator(0.5)) # minor ticks at halves
ax.yaxis.set_minor_locator(MultipleLocator(0.5)) # minor ticks at halves
# Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
cmap = mcolors.LinearSegmentedColormap.from_list("grey-blue", ['grey', 'steelblue'])
cmap.set_over('green')
im = ax.imshow(harvest, cmap=cmap, vmin=0, vmax=1)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
plt.colorbar(im, extend='max' , ax=ax)
fig.tight_layout()
plt.show()
#plt.close()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
The above code creates a colormap that goes smoothly from grey at 0 to blue at 1. If you want special colors for values 0 and 1, and a more standard colormap for the values inbetween, you could work with an 'over', an 'under' and a 'bad' color. ('bad' is meant for values that are infinite or not-a-number.) You could make a copy of the 'harvest' matrix and change the high values to 'np.nan' to have them colored special. Unfortunately there isn't an easy way to show the bad color in the colorbar, but the 'under' and the 'over' color can be shown via extend='both'
and made rectangular (instead of triangular) with extendrect=True
.
from copy import copy
cmap = copy(plt.get_cmap('hot'))
cmap.set_under('grey')
cmap.set_over('steelblue')
cmap.set_bad('green')
im = ax.imshow(np.where(harvest <= 1, harvest, np.nan), cmap=cmap, vmin=0.000001, vmax=0.999999)
plt.colorbar(im, extend='both', extendrect=True, ticks=np.arange(0, 1.01, .1), ax=ax)
Upvotes: 4
Reputation: 2082
I think plt.pcolor
is an overkill. All you have to do is to customize a colormap, and apply to imshow
. Here is my solution based on your code.
import numpy as np
from datetime import datetime as dt
from datetime import timedelta
import matplotlib
import matplotlib.pyplot as plt
from calendar import monthrange
from matplotlib.colors import ListedColormap, to_rgba
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
# you have to customize the colormap
rgba = plt.cm.viridis(range(100))
ind = int(1/harvest.max()*100)
rgba[0] = to_rgba('gray')
rgba[ind+1:] = to_rgba('green')
cmap1 = ListedColormap(rgba)
fig, ax = plt.subplots()
ax.imshow(harvest, cmap=cmap1)
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
#Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
ax.set_xticks(np.arange(harvest.shape[1]+1)-0.5, minor=True)
ax.set_yticks(np.arange(harvest.shape[0]+1)-0.5, minor=True)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
Upvotes: 3