I am plotting 3d bar plots using mplot3d:
import numpy as np
import matplotlib
matplotlib.use("Qt4Agg")
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
result=[[0, 0, 5, 5, 14,40,50],
[0, 1, 8, 9, 20,50,70],
[0, 2, 8, 10, 25,60,80],
[0, 5, 10, 20, 40,75,100]]
result = np.array(result, dtype=np.int)
fig=plt.figure()
fig.set_size_inches(6, 4)
ax1=fig.add_subplot(111, projection='3d')
ax1.view_init(25, 280)
matplotlib.rcParams.update({'font.size': 12})
matplotlib.rcParams['font.weight']='normal'
xlabels = np.array(["Count1", "Count3","Count5", "Count6","Count7","Count8","Count9"])
xpos = np.arange(xlabels.shape[0])
ylabels = np.array(["5%","10%","20%","100%"])
ypos = np.arange(ylabels.shape[0])
xposM, yposM = np.meshgrid(xpos, ypos, copy=False)
zpos=result
zpos = zpos.ravel()
dx=0.75
dy=0.5
dz=zpos
ax1.w_xaxis.set_ticks(xpos + dx/2.)
ax1.w_xaxis.set_ticklabels(xlabels)
ax1.w_yaxis.set_ticks(ypos + dy/2)
ax1.set_yticklabels(ylabels)
ax1.w_zaxis.set_ticklabels(["","20%","40%","60%","80%","100%"])
colors = ['b','b','b','b','b','b','b','r','r','r','r','r','r','r','y','y','y','y','y','y','y','g','g','g','g','g','g','g']
ax1.bar3d(xposM.ravel(), yposM.ravel(), dz*0, dx, dy, dz, color=colors)
fig.savefig('tmp.tiff', dpi=300)
plt.close()
and here is what i got:
There are two problems here actually:
1) the y tick labels do not display correctly, they are supposed to be in the middle of the ticks but instead below the ticks. z tick labels are too close to the z ticks.
2) I suppose to use the font size 12 and the dpi should be higher than 300. I could not scale x axis such that the x tick labels fit nicely and do not overlap. I have tried multiply the xpos by 2. However the tick labels still overlap.
Related
I am new to python programming. I was trying to make two subplots using matplotlib containing a line plot (panel-a) and 2-D color plot using imshow() (panel-b). I want the colorbar to be shown on the right side with same size as the color plot and it should not be within the subplot box limit.
`
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Panel (a)
x1 = np.linspace(2, -2, 5)
y1 = np.linspace(-2, 2, 5)
# Panel (b)
N = 10
arr = np.random.random((N, N))
x_lims = list(map(dt.datetime.fromtimestamp, [982376726, 982377321]))
x_lims = mdates.date2num(x_lims)
y_lims = [0, 40]
fig, ax = plt.subplots(2, 1, figsize=(14, 10))
ax[0].plot(x1, y1)
ax[0].set_ylim(-2, 2)
ax[0].set_xlim(2, -2)
ax[0].set_xticks([2, 1, 0, -1, -2])
ax[0].set_yticks([-2, -1, 0, 1, 2])
im = ax[1].imshow(arr, extent=[x_lims[0], x_lims[1], y_lims[0],
y_lims[1]],
aspect='auto')
divider = make_axes_locatable(ax[1])
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax, label="diff. en. flux")
ax[1].xaxis_date()
date_format = mdates.DateFormatter('%H:%M:%S')
ax[1].xaxis.set_major_formatter(date_format)
I have a subplot and its tick labels overlap with the data. I would like to set the x-tick labels to have a background colour (e.g. white). Currently I have only been able to find how to change the label's colour, but not the background. I know how to get the effect using a text object as shown below. (NB - I don't want the whole subplot's margin to be coloured, but just the tick label).
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""\usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
from numpy import linspace, sin
x = linspace(0, 1, 100)
plt.clf()
ax1 = plt.gca()
ax2 = plt.axes([0, 0, 1, 1], label=str(2))
ip = InsetPosition(ax1, [0.08, 0.63, 0.45, 0.3])
ax2.set_axes_locator(ip)
ax1.plot(x, x)
ax1.plot(x, x + 0.3)
ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax2.xaxis.set_tick_params(labelcolor='r')
ax1.text(0.3, 0.3, '$1$', transform=ax1.transAxes, horizontalalignment='center', verticalalignment='center', color='black', backgroundcolor='white')
To set a label's background color you may use the same property as for a text, essentially because a label is a text.
plt.setp(ax2.get_xticklabels(), backgroundcolor="limegreen")
For more sophisticated backgrounds, you could also use the bbox property.
bbox = dict(boxstyle="round", ec="limegreen", fc="limegreen", alpha=0.5)
plt.setp(ax2.get_xticklabels(), bbox=bbox)
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.plot(np.linspace(0, 1, 5), np.random.rand(5))
# set xticklabels
xtl = []
for x in ax.get_xticks():
xtl += ['lbl: {:.1f}'.format(x)]
ax.set_xticklabels(xtl)
# modify labels
for tl in ax.get_xticklabels():
txt = tl.get_text()
if txt == 'lbl: 1.0':
txt += ' (!)'
tl.set_backgroundcolor('C3')
tl.set_text(txt)
I have been following the example provided in:
https://matplotlib.org/examples/api/barchart_demo.html
My problem is that I want to add edges to the bars. But when I set the
linewidth=1, edgecolor='black'
parameters, the edges are only applied to the first pair of bars, leaving the remaining pairs unchanged.
"""
========
Barchart
========
A bar plot with errorbars and height labels on individual bars
"""
import numpy as np
import matplotlib.pyplot as plt
N = 5
men_means = (20, 35, 30, 35, 27)
men_std = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r', yerr=men_std,linewidth=1, edgecolor='black')
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y', yerr=women_std, linewidth=1, edgecolor='black')
# add some text for labels, title and axes ticks
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
ax.legend((rects1[0], rects2[0]), ('Men', 'Women'))
def autolabel(rects):
"""
Attach a text label above each bar displaying its height
"""
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width()/2., 1.05*height,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1)
autolabel(rects2)
plt.show()
Thanks for your help.
David.
I use bar3d() to plot a 3D barchart, and I'd like to flip the y axis. I've tried to use invert_yaxis(), but it seems effectless. I've also tried manually reverse the values in the list with [::-1], but it didn't help either. It keeps displaying the 3D barchart in the very same way.
Any idea how can I flip the y axis?
Here's an example how it's not working for me (not even with 3D line plots):
from matplotlib.pyplot import *
from mpl_toolkits.mplot3d.axes3d import Axes3D
fig1 = figure(1)
ax11 = subplot(2, 2, 1, projection='3d')
ax11.plot([1, 2, 3, 4], [1, 2, 3, 4])
ax12 = subplot(2, 2, 2, projection='3d')
ax12.invert_xaxis()
ax12.plot([1, 2, 3, 4], [1, 2, 3, 4])
ax21 = subplot(2, 2, 3)
ax21.plot([1, 2, 3, 4])
ax22 = subplot(2, 2, 4)
ax22.invert_xaxis()
ax22.plot([1, 2, 3, 4])
show()
And the plot looks like this: http://we.tl/cqSsecVy6P
Thanks,
Daniel
If I understand the question correctly I think the problem is that matplotlib rotates the 3D plot. To remedy this just set the initial viewing angle using ax.view_init(elev, azim). Taking the matplotlib hist3d demo then we just have
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x, y = np.random.rand(2, 100) * 4
hist, xedges, yedges = np.histogram2d(x, y, bins=4)
elements = (len(xedges) - 1) * (len(yedges) - 1)
xpos, ypos = np.meshgrid(xedges[:-1]+0.25, yedges[:-1]+0.25)
xpos = xpos.flatten()
ypos = ypos.flatten()
zpos = np.zeros(elements)
dx = 0.5 * np.ones_like(zpos)
dy = dx.copy()
dz = hist.flatten()
ypos_inv = ypos
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average')
ax.view_init(ax.elev, ax.azim+90)
plt.show()
Here I have rotated the axis by 90 degrees which flips one of the axis but not the other.
I'm rather new to both python/matplotlib and using it through the ipython notebook. I'm trying to add some annotation lines to an existing graph and I can't figure out how to render the lines on a graph. So, for example, if I plot the following:
import numpy as np
np.random.seed(5)
x = arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
p = plot(x, y, "o")
I get the following graph:
So how would I add a vertical line from (70,100) up to (70,250)? What about a diagonal line from (70,100) to (90,200)?
I've tried a few things with Line2D() resulting in nothing but confusion on my part. In R I would simply use the segments() function which would add line segments. Is there an equivalent in matplotlib?
You can directly plot the lines you want by feeding the plot command with the corresponding data (boundaries of the segments):
plot([x1, x2], [y1, y2], color='k', linestyle='-', linewidth=2)
(of course you can choose the color, line width, line style, etc.)
From your example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")
# draw vertical line from (70,100) to (70, 250)
plt.plot([70, 70], [100, 250], 'k-', lw=2)
# draw diagonal line from (70, 90) to (90, 200)
plt.plot([70, 90], [90, 200], 'k-')
plt.show()
It's not too late for the newcomers.
plt.axvline(x, color='r') # vertical
plt.axhline(x, color='r') # horizontal
It takes the range of y as well, using ymin and ymax.
Using vlines:
import numpy as np
np.random.seed(5)
x = arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
p = plot(x, y, "o")
vlines(70,100,250)
The basic call signatures are:
vlines(x, ymin, ymax)
hlines(y, xmin, xmax)
Rather than abusing plot or annotate, which will be inefficient for many lines, you can use matplotlib.collections.LineCollection:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")
# Takes list of lines, where each line is a sequence of coordinates
l1 = [(70, 100), (70, 250)]
l2 = [(70, 90), (90, 200)]
lc = LineCollection([l1, l2], color=["k","blue"], lw=2)
plt.gca().add_collection(lc)
plt.show()
It takes a list of lines [l1, l2, ...], where each line is a sequence of N coordinates (N can be more than two).
The standard formatting keywords are available, accepting either a single value, in which case the value applies to every line, or a sequence of M values, in which case the value for the ith line is values[i % M].
Matplolib now allows for 'annotation lines' as the OP was seeking. The annotate() function allows several forms of connecting paths and a headless and tailess arrow, i.e., a simple line, is one of them.
ax.annotate("",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
arrowprops=dict(arrowstyle="-",
connectionstyle="arc3, rad=0"),
)
In the documentation it says you can draw only an arrow with an empty string as the first argument.
From the OP's example:
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")
# draw vertical line from (70,100) to (70, 250)
plt.annotate("",
xy=(70, 100), xycoords='data',
xytext=(70, 250), textcoords='data',
arrowprops=dict(arrowstyle="-",
connectionstyle="arc3,rad=0."),
)
# draw diagonal line from (70, 90) to (90, 200)
plt.annotate("",
xy=(70, 90), xycoords='data',
xytext=(90, 200), textcoords='data',
arrowprops=dict(arrowstyle="-",
connectionstyle="arc3,rad=0."),
)
plt.show()
Just as in the approach in gcalmettes's answer, you can choose the color, line width, line style, etc..
Here is an alteration to a portion of the code that would make one of the two example lines red, wider, and not 100% opaque.
# draw vertical line from (70,100) to (70, 250)
plt.annotate("",
xy=(70, 100), xycoords='data',
xytext=(70, 250), textcoords='data',
arrowprops=dict(arrowstyle="-",
edgecolor = "red",
linewidth=5,
alpha=0.65,
connectionstyle="arc3,rad=0."),
)
You can also add curve to the connecting line by adjusting the connectionstyle.