Related
I am trying to plot a dashed line in a 3-D Matplotlib plot. I would like to get a dashed line between each (x_pt, y_pt) to its corresponding z_pt.
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib
matplotlib.rcParams['mathtext.fontset'] = 'cm'
matplotlib.rcParams['axes.labelsize'] = 13
def z_function(x, y):
a = 1
b = 5.1/(4*np.pi**2)
c = 5/np.pi
r = 6
s = 10
t = 1/(8*np.pi)
return a*(y - b*x**2 + c*x - r)**2 + s*(1 - t)*np.cos(x) + s
x = np.linspace(-5, 10, 100)
y = np.linspace(0, 15, 100)
indexes = np.random.randint(0, 100, 5)
x_pt = x[indexes]
y_pt = y[indexes]
z_pt = z_function(x_pt, y_pt)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x_pt, y_pt, color='k', marker='x', depthshade=False)
ax.scatter(x_pt, y_pt, z_pt, color='k', marker='^', depthshade=False)
ax.set_xticks([-5, 0, 5, 10])
ax.set_yticks([0, 5, 10, 15])
ax.set_zticks([100, 200, 300])
ax.view_init(30, -120)
ax.set_xlabel(r'$x_1$')
ax.set_ylabel(r'$x_2$')
ax.zaxis.set_rotate_label(False)
ax.set_zlabel(r'$f(x)$', rotation=0)
ax.w_xaxis.pane.fill = False
ax.w_yaxis.pane.fill = False
ax.w_zaxis.pane.fill = False
plt.show()
Can anyone help me with this?
If I understand your problem correctly, you need to connect the point (x,y,0) to (x,y,z) like so:
for x_,y_,z_ in zip(x_pt, y_pt, z_pt):
ax.plot([x_,x_],[y_,y_],[0,z_], '--', c='grey')
It should be as simple as:
ax.plot(x_pt, y_pt, zs=z_pt, color='blue', marker='--', depthshade=False)
alternatively using:
ax.plot3D(x_pt, y_pt, z_pt, marker='--')
UPDATE:
You will need to create extra dummy coordinates for each point on the x-y axis, like so:
import numpy as np
n = 10 # number of points in the line
for i in len(x_pt):
x_range = np.linspace(0, x_pt[i], n)
y_range = np.linspace(0, y_pt[i], n)
ax.plot3D(x_range, y_range, [z_pt[i]]*n, marker='--')
NOTE: Untested
In Python matplotlib, how can you get the line in a line or step plot to display a gradient based on the y-value?
Example plot (made in Tableau):
Code for step plot with a line that changes gradient according to x-value, adapted from this answer:
fig, ax = plt.subplots(figsize=(10, 4))
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
y = [2, 3, 9, 10, 2, 9, 0, 1, 9, 1, -8]
T = np.linspace(0,1,np.size(x))**2
s = 1
for i in range(0, len(x)-s, s):
ax.step(x[i:i+s+1], y[i:i+s+1], marker='.', color=(0.0,0.5,T[i]))
ax.tick_params(axis='both', colors='lightgray', labelsize=8)
The following code is inspired by the multicolored-line example from the matplotlib docs. First the horizontal line segments are drawn and colored using their y-value. The vertical segments are subdivided in small chunks to colored individually.
vmin of the norm is set a bit lower to avoid the too-light range of the colormap.
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
x = np.arange(50)
y = np.random.randint(-3, 4, x.size).cumsum()
fig, ax = plt.subplots()
norm = plt.Normalize(y.min() - y.ptp() * .2, y.max())
cmap = 'inferno_r' # 'Reds'
horizontal_lines = np.array([x[:-1], y[:-1], x[1:], y[:-1]]).T.reshape(-1, 2, 2)
hor_lc = LineCollection(horizontal_lines, cmap=cmap, norm=norm)
hor_lc.set_array(y[:-1])
ax.add_collection(hor_lc)
factor = 10
long_y0 = np.linspace(y[:-1], y[1:], factor)[:-1, :].T.ravel()
long_y1 = np.linspace(y[:-1], y[1:], factor)[1:, :].T.ravel()
long_x = np.repeat(x[1:], factor - 1)
vertical_lines = np.array([long_x, long_y0, long_x, long_y1]).T.reshape(-1, 2, 2)
ver_lc = LineCollection(vertical_lines, cmap=cmap, norm=norm)
ver_lc.set_array((long_y0 + long_y1) / 2)
ax.add_collection(ver_lc)
ax.scatter(x, y, c=y, cmap=cmap, norm=norm)
plt.autoscale() # needed in case the scatter plot would be omited
plt.show()
Here is another example, with a black background. In this case the darkest part of the colormap is avoided. The changed code parts are:
y = np.random.randint(-9, 10, x.size)
ax.patch.set_color('black')
norm = plt.Normalize(y.min(), y.max() + y.ptp() * .2)
cmap = 'plasma_r'
Here is an example with a TwoSlopeNorm and the blue-white-red colormap:
from matplotlib.colors import TwoSlopeNorm
y = np.random.uniform(-1, 1, x.size * 10).cumsum()[::10]
y = (y - y.min()) / y.ptp() * 15 - 5
norm = TwoSlopeNorm(vmin=-5, vcenter=0, vmax=10)
cmap = 'bwr'
as you can see, I want to make the dash connect to the x and y axes.
There is always a small gap.
I use matplotlib
the vline function, and I don't know how to use the transform parameters.
Using vlines and hlines from matplotlib.pyplot, you can specify your axes and your line limits:
from matplotlib import pyplot as plt
# Drawing example diagram
plt.scatter(x=11,y=0.891)
plt.xlim(5,20)
plt.xticks([5,8,11,14,17,20])
plt.ylim(0.780,0.9)
# Specifying lines, notice how despite setting xmin and ymin lower than your axes,
# the lines stop at each boundary
plt.vlines(x=11, ymin=0.7, ymax=0.891, colors='r',linestyles='dashed')
plt.hlines(y=0.891, xmin=4, xmax=11, colors='k',linestyles='dashed')
plt.show()
The result is beautiful, but the code not so good.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as ticker
x = [i for i in range(5, 21, 3)]
# [5, 8, 11, 14, 17, 20]
y = [0.780, 0.865, 0.891, 0.875, 0.884, 0.870]
y_max_index = np.argmax(y)
# print(y_max_index)
# get the max point
x_max = x[y_max_index]
y_max = y[y_max_index]
fig, ax = plt.subplots()
ax.plot(x, y, marker='o', color='r')
# set x ticks as [5, 8, 11, 14, 17, 20]
my_x_ticks = x
plt.xticks(my_x_ticks)
# set x and y lim
axe_y_min, axe_y_max = ax.get_ylim()
axe_x_min, axe_x_max = ax.get_xlim()
ax.set_ylim(axe_y_min, axe_y_max)
ax.set_xlim(axe_x_min, axe_x_max)
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%.3f')) # set y axe format
anno_text = "(11, 0.891)"
plt.annotate(anno_text, xy=(x_max, y_max), xytext=(x_max+0.5, y_max)) # annotate
y_scale_trans = (y_max - axe_y_min) / (axe_y_max - axe_y_min)
x_scale_trans = (x_max - axe_x_min) / (axe_x_max - axe_x_min)
ax.vlines(x_max, 0, y_scale_trans, transform=ax.get_xaxis_transform(), colors='black', linestyles="dashed")
ax.hlines(y_max, 0, x_scale_trans, transform=ax.get_yaxis_transform(), colors='black', linestyles="dashed")
plt.ylabel("准确率")
plt.xlabel("滑动窗口大小")
plt.savefig("滑动窗口.pdf", dpi=100)
plt.show()
Here is a solution using plt.plot to draw the lines.
import matplotlib.pyplot as plt
import numpy as np
y = np.random.randint(1, 10, 10)
x = np.arange(len(y))
point = [x[2], y[2]]
plt.plot(x,y)
plt.plot((point[0], point[0]), (0, point[1]), '--')
plt.plot((0, point[0]), (point[1], point[1]), '--')
plt.xlim(0,10)
plt.ylim(0,10)
When I plot data using matplotlib I always have 5-9 ticks on my x-axis independent of the range I plot, and if I zoom on the x-axis the tick spacing decreases, so I still see 5-9 ticks.
however, I would like 20-30 ticks on my x-axis!
I can achieve this with the following:
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
number_of_ticks_on_x_axis = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/number_of_ticks_on_x_axis))
plt.show()
If I now zoom on the x-axis, no new ticks appear between the existing ones. I would like to still have ~20 ticks however much I zoom.
Assuming that you want to fix the no. of ticks on the X axis
...
from matplotlib.ticker import MaxNLocator
...
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(MaxNLocator(15, min_n_ticks=15))
...
Please look at the docs for MaxNLocator
Example
In [36]: import numpy as np
...: import matplotlib.pyplot as plt
In [37]: from matplotlib.ticker import MaxNLocator
In [38]: fig, ax = plt.subplots(figsize=(10,4))
In [39]: ax.grid()
In [40]: ax.xaxis.set_major_locator(MaxNLocator(min_n_ticks=15))
In [41]: x = np.linspace(0, 1, 51)
In [42]: y = x*(1-x)
In [43]: plt.plot(x, y)
Out[43]: [<matplotlib.lines.Line2D at 0x7f9eab409e10>]
gives
and when I zoom into the maximum of the curve I get
You can link a callback function to an event in the canvas. In you case you can trigger a function that updates the axis when a redraw occurs.
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
n = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/n), rotation=90)
def on_zoom(event):
ax = plt.gca()
fig = plt.gcf()
x_min, x_max = ax.get_xlim()
ax.set_xticks(np.linspace(x_min, x_max, n))
# had to add flush_events to get the ticks to redraw on the last update.
fig.canvas.flush_events()
fig = plt.gcf()
fig.canvas.mpl_disconnect(cid)
cid = fig.canvas.mpl_connect('draw_event', on_zoom)
I have 2 lists, each has 128 elements
x = [1,2,3,...,128]
y = [y1,y2,...,y128]
How should I use matplotlib to plot (x,y) with x axis appearing as shown in this screenshot?
To replicate the graph, I have (1) created 2 additional lists from the original lists, and (2) used set_xticklabels:
f, ax1 = plt.subplots(1,1,figsize=(16,7))
x1 = [1, 2, 4, 8, 16, 32, 64, 128]
y1 = [y[0],y[1],y[3],y[7],y[15],y[31],y[63],y[127]]
line1 = ax1.plot(x1,y1,label="Performance",color='b',linestyle="-")
ax1.set_xticklabels([0,1,2,4,8,16,32,64,128])
ax1.set_xlabel('Time Period',fontsize=15)
ax1.set_ylabel("Value",color='b',fontsize=15)
The problem with this approach is that only 8 pairs of value are plotted, and 120 pairs are ommitted.
If my comments aren't clear enough, please, ask. :)
from matplotlib import pyplot as plt
# Instanciating my lists...
f = lambda x:x**2
x = [nb for nb in range(1, 129)]
y = [f(nb) for nb in x]
# New values you want to plot, with linear spacing.
indexes_to_keep = [1, 2, 4, 8, 16, 32, 64, 128]
y_to_use = [y[nb - 1] for nb in indexes_to_keep]
# First plot that shows the 128 points as a whole.
fig = plt.figure(figsize=(10, 5.4))
ax1 = fig.add_subplot(121)
ax1.plot(x, y)
ax1.set_title('Former values')
# Second plot that shows only the indexes you wish to keep.
ax2 = fig.add_subplot(122)
# my_ticks = [1, 2, 3, 4, 5, 6, 7]
# meaning : my_ticks will be linear values.
my_ticks = [i for i in range(len(indexes_to_keep))]
# We set the ticks we want to show, meaning : all our list
# instead of some linear spacing matplotlib will show by default
ax2.set_xticks(my_ticks)
# Then, we manually change the name of the X ticks.
ax2.set_xticklabels(indexes_to_keep)
# We will then, plot the LINEAR x axis,
# but with respect to the y-axis values pre-processed.
ax2.plot(my_ticks, y_to_use)
ax2.set_title('New selected values with linear spacing')
plt.show()
Showing...
What you are looking for is a logarithmic scale with base 2. matplotlib provides logarithmic scales and you can define any base you want:
from matplotlib import pyplot as plt
from matplotlib.ticker import ScalarFormatter
#sample data
x = list(range(1, 130))
y = list(range(3, 260, 2))
f, ax1 = plt.subplots(1,1,figsize=(16,7))
x1 = [ 1, 2, 4, 8, 16, 32, 64, 128]
y1 = [y[0],y[1],y[3],y[7],y[15],y[31],y[63],y[127]]
#just the points, where the ticks are
ax1.plot(x1, y1,"bo-", label = "Performance")
#all other points to contrast this
ax1.plot(x, [270 - i for i in y], "rx-", label = "anti-Performance")
#transform x axis into logarithmic scale with base 2
plt.xscale("log", basex = 2)
#modify x axis ticks from exponential representation to float
ax1.get_xaxis().set_major_formatter(ScalarFormatter())
ax1.set_xlabel('Time Period',fontsize=15)
ax1.set_ylabel("Value",color='b',fontsize=15)
plt.legend()
plt.show()
Output: