I have a dataset like this,where I have a set of values for xs and I plot the corresponding line graph with the values of ys.
xs = np.array([1,2,5,6,9,10,11)
ys = pow(xs,2)
ys
plt.plot(xs, ys, linestyle='-', marker='o')
plt.show()
If you notice by default, plot connects the points and draws line. But, I want to draw the line at 0 for missing points. How do I do this ? Should I manipulate the data to fill missing values with zeros (numpy,maybe) or is there a way to plot this matplotlib.plot ?
To be precise I need to plot: xs = np.array([1,2,0,0,5,6,0,0,9,10,11,0,0,0,0])
ys = pow(xs,2)
But, as of now, this is my xs=np.array([1,2,5,6,9,10,11). How do i fill the missing elements in the range 1:15. I looked at masked_array which is different. Is there any other fill option in numpy ?
Since you want to plot points that aren't in your data set, it will be hard to do directly in matplotlib. But, constructing the points is easy enough using put:
xs = array([1,2,5,6,9,10,11])
ys = xs**2
x = arange(12)
y = zeros(12, dtype=int32)
put(y, xs, ys)
plt.plot(x, y, 'o', clip_on=False)
If you aren't dealing with an integer X axis, you can do this:
xs = array([1.0,2,5,6,9,10,11])
ys = xs**2
x = arange(0, 12, 0.5)
y = zeros(x.shape)
mask = r_[diff(searchsorted(xs, x)), 0]
y[mask == 1] = ys
plt.plot(x, y, 'o', clip_on=False)
Related
I'm trying to visualize data where each X value has multiple Y values and I would like to distinguish each Y value visaully. This is the example code
xLables = ['A1','A2','A3','A4','A5']
YValues = [[1,2,3,4],[1,2,3,4,5,6,7],[1,2,3],[5,6,7],[1,2,3]]
X = [xLables[i] for i, data in enumerate(YValues) for j in range(len(data))]
Y = [val for data in YValues for val in data]
plt.scatter(X, Y)
plt.grid()
plt.show()
When I plot this , I get the following attached
Each X label has corresponding Y values ... For Ex: A1 has 1,2,3,4 , A2 has 1,2,3,4,5,6,7 and so on
I have two questions on this one
(1) Can I have different markers for different Y-values .. all 1's are stars , all 2's are diamonds , all 10's are circles ?
something like this may be
(2) Is there a better way to plot such 2D data and distingush them where each X has multiple Y values
Any suggestions/help is highly appreciated
Thanks
I tried to add markers and different colors , but they apply to all Y values for each X .. but not specific to each Y values..
My solution is particularly ad hoc, but it replicates your target drawing using your data, so that I feel confident posting her here.
import matplotlib.pyplot as plt
labels = ['A1','A2','A3','A4','A5']
Y2D = [[1,2,3,4],[1,2,3,4,5,6,7],[1,2,3],[5,6,7],[1,2,3]]
# prepare a dictionary with the characteristics
# we want to change according to the Y value
d = {1:dict(marker="*", s=150, color="red"),
2:dict(marker="o", s=100, color="yellow"),
3:dict(marker="o", s= 60, color="blue"),
4:dict(marker="o", s=100, color="green"),
5:dict(marker="o", s=100, color="red"),
6:dict(marker="*", s=150, color="blue"),
7:dict(marker="o", s=100, color="lightgray")}
# an outer loop on the abscissae and the lists of Y values
for x, ys in zip(labels, Y2D):
an inner loop on the Y values, plotted separately
for y in ys:
# here the point is to unpack the values contained
# in the "inner" dictionary, addressing the outer by Y
# zorder=5 places the dots above the grid
plt.scatter(x, y, ec='k', zorder=5, **d[y])
plt.grid(1)
plt.show()
I think the easiest way is to use one plt.scatter() per score.
import matplotlib.pyplot as plt
xLables = [ 'A1','A2','A3','A4','A5']
YValues = [ [1,2,3,4],[1,2,3,4,5,6,7],[1,2,3],[5,6,7],[1,2,3]]
markers = [ '.', 'o', '^', 'v', '>', '<', '*'] # to be customized
Y = [None for i in range( len( xLables))]
for y in range( len( markers)):
for x in range( len( xLables)):
Y[x] = y+1 if y+1 in YValues[x] else None # values start at 1
if any( Y): # something to display?
plt.scatter( xLables, Y, marker=markers[y])
plt.grid()
plt.show()
Add your marker types to list & iterate over them accordingly.
from matplotlib import pyplot as plt
import matplotlib
xLables = ['A1','A2','A3','A4','A5']
YValues = [[1,2,3,4],[1,2,3,4,5,6,7],[1,2,3],[5,6,7],[1,2,3]]
X = [xLables[i] for i, data in enumerate(YValues) for j in range(len(data))]
Y = [val for data in YValues for val in data]
plt.scatter(X, Y, marker=matplotlib.markers.CARETDOWNBASE)
plt.grid()
markers=['8','+', '.', 'o', '*','^', 's', 'p', 'h','8','+', '.', 'o', '*','^', 's', 'p', 'h' ]
for i in range(18):
plt.plot(X[i], Y[i], marker=markers[i])
plt.xlabel('X Label')
plt.ylabel('Y Label')
plt.show()
Output:
Note: change the order you want accordingly you want. That will be replicated over graphs
You can add markers.
A list of them can be found here: https://matplotlib.org/stable/api/markers_api.html
I think you can use it like this:
plt.scatter([1, 2, 3], marker=11) which means that you'll have to put the values you want to be the same in the same list. I don't think there is a way of giving a list of markers or something like that.
As far as i understand your code would look something like:
plt.scatter(X, Y[0], marker=1)
plt.scatter(X, Y[1], marker=1)
plt.scatter(X, Y[2], marker=1)
plt.scatter(X, Y[3], marker=1)
You could make this a for loop if it is something having to work for different sizes but i suppose you'll figure it out from here.
Good luck i hope this helps.
I have measured the positions of different products in different angles positions (6 values in steps of 60 deg. over a complete rotation). Instead of representing my values on a Cartesian graph where 0 and 360 are the same point, I want to use a polar graph.
With matplotlib, I got a spider chart type graph, but I want to avoid straight lines between points and display and extrapolated values between those. I have a solution that is kind of OK, but I was hoping there is a nice "one liner" I could use to have a more realistic representation or a better tangent handling for some points.
Does anyone have an idea to improve my code below ?
# Libraries
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# Some data to play with
df = pd.DataFrame({'measure':[10, -5, 15,20,20, 20,15,5,10], 'angle':[0,45,90,135,180, 225, 270, 315,360]})
# The few lines I would like to avoid...
angles = [y/180*np.pi for x in [np.arange(x, x+45,5) for x in df.angle[:-1]] for y in x]
values = [y for x in [np.linspace(x, df.measure[i+1], 10)[:-1] for i, x in enumerate(df.measure[:-1])] for y in x]
angles.append(360/180*np.pi)
values.append(values[0])
# Initialise the spider plot
ax = plt.subplot(polar=True)
# Plot data
ax.plot(df.angle/180*np.pi, df['measure'], linewidth=1, linestyle='solid', label="Spider chart")
ax.plot(angles, values, linewidth=1, linestyle='solid', label='what I want')
ax.legend()
# Fill area
ax.fill(angles, values, 'b', alpha=0.1)
plt.show()
the result is below, I want something similar to the orange line with some kind of spline to avoid sharp corners I currently get
I have a solution that is a patchwork of other solutions. It needs to be cleaned and optimized, but it does the job !
Comments and improvements are always welcome, see below
# https://stackoverflow.com/questions/33962717/interpolating-a-closed-curve-using-scipy
from scipy import interpolate
x=df.measure[:-1] * np.cos(df.angle[:-1]/180*np.pi)
y=df.measure[:-1] * np.sin(df.angle[:-1]/180*np.pi)
x = np.r_[x, x[0]]
y = np.r_[y, y[0]]
# fit splines to x=f(u) and y=g(u), treating both as periodic. also note that s=0
# is needed in order to force the spline fit to pass through all the input points.
tck, u = interpolate.splprep([x, y], s=0, per=True)
# evaluate the spline fits for 1000 evenly spaced distance values
xi, yi = interpolate.splev(np.linspace(0, 1, 1000), tck)
def cart2pol(x, y):
rho = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
return(rho, phi)
# Initialise the spider plot
plt.figure(figsize=(12,8))
ax = plt.subplot(polar=True)
# Plot data
ax.plot(df.angle/180*np.pi, df['measure'], linewidth=1, linestyle='solid', label="Spider chart")
ax.plot(angles, values, linewidth=1, linestyle='solid', label='Interval linearisation')
ax.plot(cart2pol(xi, yi)[1], cart2pol(xi, yi)[0], linewidth=1, linestyle='solid', label='Smooth interpolation')
ax.legend()
# Fill area
ax.fill(angles, values, 'b', alpha=0.1)
plt.show()
I'm plotting something similar to a spectogram. I have some 1D arrays of length N, each of which corresponds to one 'horizontal line' in a 2D graph. For a spectogram [M,N], I would need M such N-length arrays to fill all M horizontal lines.
However, I only have data for a smaller number of lines. Let's say I have m < M arrays. These don't correspond to equal-spaced values in the y axis. They're random values. For example, I might only have arrays corresponding to lines 6, 44, 44.5 and 92 (where M=4).
I want to have the y axis from 0 to 100, and plot these lines of values for only the y-values I have, and 0 otherwise. How do I do this?
If I had arrays for y values that are equally spaced on the Y axis, I can do this:
y_values = np.array(M) # ticks for y axis - equal-spaced. eg: [2, 2.5, 3, 3.5]. These are the only values for which data exists.
get_y_values(y_values)
data = np.array([M,N])
get_data(data)
fig = pyplot.figure()
vmax = np.amax(data)
ax = fig.add_subplot(1, 1, 1)
ax.imshow(data, origin='lower', aspect='auto', vmin=0, vmax=vmax)
ax.set_xlabel('Time')
ax.set_ylabel('Frequency')
ax.set_yticks(np.arange(0, y_values.size))
ax.set_yticklabels(yvalues)
pyplot.show()
But it won't work for random y-values, as they'll appear equal-spaced -when they aren't.
There are a number of ways to do this, but a quick and easy way would be to loop over each time series and plot them individually.
import numpy as np
import matplotlib.pyplot as plt
M = 4
N = 100
dy = 2.0 # The desired vertical thickness of each line
y_values = np.arange(0,100)
y_values = [6, 44, 47, 92]
x = np.arange(0, N)
data = np.random.rand(M,N)
fig = plt.figure()
vmax = np.amax(data)
ax = fig.add_subplot(1, 1, 1)
for i in range(M):
ax.pcolor(x, [y_values[i], y_values[i] + dy], [data[i],data[i]], vmin=0, vmax=vmax)
ax.set_xlabel('Time')
ax.set_ylabel('Frequency')
ax.set_yticks(y_values)
ax.set_ylim(0,100)
plt.show()
Here's a link to the output figure.
I set out to solve the problem of filling in the missing data points. This code assumes you want a list of xs from 0 to 100 and that you already have a few xs and a few corresponding ys. It then fills in the rest of the xs and sets their corresponding y values to 0. After that, it orders them by x values, zips, and prints. I figured you can adapt this from here. Hopefully, I haven't misunderstood.
current_xs = [6, 44, 44.5, 92] #the x values
current_ys = [7, 2, 45, 5] #corresponding y values I made up
def fill(current_xs, current_ys):
for i in range(0, 200):
if i/2 not in current_xs:
current_xs.append(i/2)
current_ys.append(0)
total = list(zip(current_xs, current_ys))
total = sorted(total, key=lambda x: x[0])
print(total)
fill(current_xs, current_ys)
I want to generate a heatmap using Python.
The map should be like this:
I have a numpy array with dimension (n,n) and each "cell" contains a certain value. The higher higher that value is, the bigger a pink square should be.
How can I plot this kind of chart using matplotlib? Are there other libraries that I can use?
Thank you.
You could try this
n = 8
x = np.arange(n)
y = np.arange(n)
X, Y = np.meshgrid(x, y)
Z = np.random.randint(0, 800, (len(x), len(y)))
plt.figure()
plt.axes(aspect='equal')
plt.scatter(X+.5, Y+.5, Z, 'pink', marker='s')
plt.grid()
plt.xlim(0, n)
plt.ylim(0, n)
plt.tick_params(labelsize=0, length=0)
I actually want to recreate an image like the following:
Specially the little X on the xaxes
I have a
list = [[100,-3],[200,None],[120,-2] ... ]
and I do
for x in list:
if x[1]!=None:
plot(x[0],x[1],'ok')
else:
### PLot on the axes ###
But while I am plotting I do not know what the axes are. I know that some values are None, for example ( 250,None), So I want to plot on the xaxes at x = 250, but I have not idea what eventually the min(ylim()) will be.
I know I can do plot(250,-5,'X',zorder=999999) but this is only when I know what the min axes is.. (I can not do min, max and so to know the min axes. as the real data is a list inside a list inside a dictionary etc.. )
So the trick is to use a custom transformation. The regular data transformation for the x axis and the axes transformation for the y axis. Matplotlib calls that a blended transformation, which you need to create yourself. You'll find more information in this awesome guide.
And as #ThePredator already pointed out, you have to set clip_on=False, otherwise your markers will be clipped.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
fig, ax = plt.subplots()
# the x coords of this transformation are data, and the
# y coord are axes
trans = transforms.blended_transform_factory( ax.transData, ax.transAxes)
# data points on the axes
x = np.random.rand(5)*100. + 200.
y = [0]*5
ax.plot(x, y, 'kx', transform=trans, markersize=10, markeredgewidth=2,
clip_on=False)
# regular data
x = np.random.rand(5)*100. + 200.
y = np.random.rand(5)*100. + 200.
ax.plot(x, y, 'ro')
plt.show()
Result:
You can use the clip_on = False option. Example:
In your case, you can set your y limits.
Example:
x = [0,1,2,3,4,5]
y = [0,0,0,0,0,0]
plt.plot(x,y,'x',markersize=20,clip_on=False,zorder=100)
plt.ylim(0,1)
plt.show()
You can use get_ylim() in order to get the position of the axis and then plot on it.