Python - Line colour of 3D parametric curve - python

I have 2 lists tab_x (containe the values of x) and tab_z (containe the values of z) which have the same length and a value of y.
I want to plot a 3D curve which is colored by the value of z. I know it's can be plotted as a 2D plot but I want to plot a few of these plot with different values of y to compare so I need it to be 3D.
My tab_z also containe negatives values
I've found the code to color the curve by time (index) in this question but I don't know how to transforme this code to get it work in my case.
Thanks for the help.
I add my code to be more specific:
fig8 = plt.figure()
ax8 = fig8.gca(projection = '3d')
tab_y=[]
for i in range (0,len(tab_x)):
tab_y.append(y)
ax8.plot(tab_x, tab_y, tab_z)
I have this for now
I've tried this code
for i in range (0,len(tab_t)):
ax8.plot(tab_x[i:i+2], tab_y[i:i+2], tab_z[i:i+2],color=plt.cm.rainbow(255*tab_z[i]/max(tab_z)))
A total failure:

Your second attempt almost has it. The only change is that the input to the colormap cm.jet() needs to be on the range of 0 to 1. You can scale your z values to fit this range with Normalize.
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colors
fig = plt.figure()
ax = fig.gca(projection='3d')
N = 100
y = np.ones((N,1))
x = np.arange(1,N + 1)
z = 5*np.sin(x/5.)
cn = colors.Normalize(min(z), max(z)) # creates a Normalize object for these z values
for i in xrange(N-1):
ax.plot(x[i:i+2], y[i:i+2], z[i:i+2], color=plt.cm.jet(cn(z[i])))
plt.show()

Related

How can I get the x and y coordinate values for the region with the highest count / "hottest" in a Hexbin plot?

I'm using a line symmetry detector for a project I've found on github and it makes use of matplotlib's hexbin plot to identify coordinates to find the symmetric line.
Unfortunately, this method is very manual and requires the user to identify the x and y coordinates through the generated plot, and then input the values into the program again.
Is there a way to return the x and y values where the region is hottest in the hexbin plot?
For reference, this is the generated hexbin plot. The coordinates I am looking for is roughly x=153.0 y=1.535
plt.hexbin returns a PolyCollection object, which has the X and Y positions and their values, see here. You can access them with get_offsets() and get_array(). Here's an example:
# Figure from https://www.geeksforgeeks.org/matplotlib-pyplot-hexbin-function-in-python/
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
n = 100000
x = np.random.standard_normal(n)
y = 12 * np.random.standard_normal(n)
polycollection = plt.hexbin(x, y, gridsize = 50, cmap ='Greens')
# New part
max_ = polycollection.get_array().max()
max_pos = polycollection.get_array().argmax()
pos_x, pos_y = polycollection.get_offsets()[max_pos]
plt.text(pos_x, pos_y, max_, color='w')
This is the resulting plot:

Python: Creating a Grid of X,Y coordinates and corresponding calculated Z values to result in a 3D array of XYZ

I have a function that calculates a z value from a given x and y coordinate. I then want to combine these values together to get a 3D array of x,y,z. I'm attempting to do this with the code below:
#import packages
import pandas as pd
import math
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.tri as tri
import matplotlib.pyplot as plt
from matplotlib import rcParams
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
from mpl_toolkits.mplot3d import Axes3D
#Define function to calculate z over a grid
def func(X, Y, x, y, Q):
return (Q / (2 * np.pi)) * np.arctan((y-Y)/(x-X))
#For initial testing just defining the IW explicitly, last step will be to read the input file and pull this data
X1=2417743.658
Y1=806346.704
Q1=5
X2=2417690.718
Y2=806343.693
Q2=5
X3=2417715.221
Y3=806309.685
Q3=5
#initiate the XY grid
xi = np.linspace(2417675,2417800,625)
yi = np.linspace(806300,806375,375)
#mesh the grid in to x,y space
x,y = np.meshgrid(xi,yi)
#calculate the values over the grid at every x,y using the defined function above
zi = (func(X1,Y1,x,y,Q1)+func(X2,Y2,x,y,Q2)+func(X3,Y3,x,y,Q3))
#reshape the xy space into 3d space - when i plot this grid it looks correct
xy = np.array([[(x, y) for x in xi] for y in yi])
#reshape z into 3d space - this appears to be where the issue begins
z = np.array(zi).reshape(xy.shape[0],xy.shape[1], -1)
#combined xyz into a single grid
xyz = np.concatenate((xy, z), axis = -1)
# Create figure and add axis
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111)
img = ax.imshow((xyz*255).astype(np.uint8))
output:
I do get an XYZ array and when i print it the values appear to be mapping correctly, however when I plot the data, it shows the y values "upside down" essentially. This is what the output should look like but "flipped" over the x over axis. Additionally the axes show node numbers and not the X,Y values. I want the 0,0 point to be the lower left hand corner like cartesian coordinates, and each x,y have a corresponding z which is calculated from that given x,y. I know there must be an easier way to go about this. Does anyone know a better way? or maybe what i'm doing wrong here?
Thanks
There is an option for ax.imshow() that allows to specify the origin point.
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html
origin{'upper', 'lower'}, default: rcParams["image.origin"] (default:
'upper') Place the [0, 0] index of the array in the upper left or
lower left corner of the Axes. The convention (the default) 'upper' is
typically used for matrices and images.
Note that the vertical axis points upward for 'lower' but downward for
'upper'.
See the origin and extent in imshow tutorial for examples and a more
detailed description.
Try to modify to this:
img = ax.imshow((xyz*255).astype(np.uint8), origin='lower')
For the axis labels they can be changed with the following commands
ax.set_xticks(LIST_OF_INDICIES)
ax.set_xticklabels(LIST_OF_VALUES)

2D histogram where one axis is cumulative and the other is not

Let's say I have instances of two random variables that can be treated as paired.
import numpy as np
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
Using matplotlib it is pretty easy to make a 2D histogram.
import matplotlib.pyplot as plt
plt.hist2d(x,y)
In 1D, matplotlib has an option to make a histogram cumulative.
plt.hist(x,cumulative=True)
What I would like incorporates elements of both classes. I would like to construct a 2D histogram such that the horizontal axis is cumulative and the vertical axis is not cumulative.
Is there are way to do this with Python/Matplotlib?
You can take advantage of np.cumsum to create your cumulative histogram. First save the output from hist2d, then apply to your data when plotting.
import matplotlib.pyplot as plt
import numpy as np
#Some random data
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
#create a figure
plt.figure(figsize=(16,8))
ax1 = plt.subplot(121) #Left plot original
ax2 = plt.subplot(122) #right plot the cumulative distribution along axis
#What you have so far
ax1.hist2d(x,y)
#save the data and bins
h, xedge, yedge,image = plt.hist2d(x,y)
#Plot using np.cumsum which does a cumulative sum along a specified axis
ax2.pcolormesh(xedge,yedge,np.cumsum(h.T,axis=1))
plt.show()

Plot 2D histogram data with pcolormesh

I need to plot a binned statistic, as one would get from scipy.stats.binned_statistic_2d. Basically, that means I have edge values and within-bin data. This also means I cannot (to my knowledge) use plt.hist2d. Here's a code snippet to generate the sort of data I might need to plot:
import numpy as np
x_edges = np.arange(6)
y_edges = np.arange(6)
bin_values = np.random.randn(5, 5)
One would imagine that I could use pcolormesh for this, but the issue is that pcolormesh does not allow for bin edge values. The following will only plot the values in bins 1 through 4. The 5th value is excluded, since while pcolormesh "knows" that the value at 4.0 is some value, there is no later value to plot, so the width of the 5th bin is zero.
import matplotlib.pyplot as plt
X, Y = np.broadcast_arrays(x_edges[:5, None], y_edges[None, :5])
plt.figure()
plt.pcolormesh(X, Y, bin_values)
plt.show()
I can get around this with an ugly hack by adding an additional set of values equal to the last values:
import matplotlib.pyplot as plt
X, Y = np.broadcast_arrays(x_edges[:, None], y_edges[None, :])
dummy_bin_values = np.zeros([6, 6])
dummy_bin_values[:5, :5] = bin_values
dummy_bin_values[5, :] = dummy_bin_values[4, :]
dummy_bin_values[:, 5] = dummy_bin_values[:, 4]
plt.figure()
plt.pcolormesh(X, Y, dummy_bin_values)
plt.show()
However, this is an ugly hack. Is there any cleaner way to plot 2D histogram data with bin edge values? "No" is possibly the correct answer, but convince me that's the case if it is.
I do not understand the problem with any of the two options. So here is simly a code which uses both, numpy histogrammed data with pcolormesh, as well as simply plt.hist2d.
import numpy as np
import matplotlib.pyplot as plt
x_edges = np.arange(6)
y_edges = np.arange(6)
data = np.random.rand(340,2)*5
### using numpy.histogram2d
bin_values,_,__ = np.histogram2d(data[:,0],data[:,1],bins=(x_edges, y_edges) )
X, Y = np.meshgrid(x_edges,y_edges)
fig, (ax,ax2) = plt.subplots(ncols=2)
ax.set_title("numpy.histogram2d \n + plt.pcolormesh")
ax.pcolormesh(X, Y, bin_values.T)
### using plt.hist2d
ax2.set_title("plt.hist2d")
ax2.hist2d(data[:,0],data[:,1],bins=(x_edges, y_edges))
plt.show()
Of course this would equally work with scipy.stats.binned_statistic_2d.

x axis with duplicate values (loading profile) plot in matplotlib

i have load profile data where x axis is load profile such that for multiple same values of x (constant load) i have different values for y.
till now in excel i used to line plot y and right click graph->selec data->change hoizontal axis data by providing it range o x axis data and that used to give me the graph
the problem i have is when i try to give
plot(x,y), matplotlib plots y for unique vals of x ie it neglects out all the remaining value of for same value of x.
and when i plot with plot(y) i get sequence numbers on x axis
i tried xticks([0,5,10,15]) for checking out but couldn't get the required result.
my question is
is it possible to plot a graph in a similar fashion as of excel
the other alternative i could think of was plotting plot(y and plot (x) with same horizontal axis it atleast gives a pictorial idea but is there any means to do it the excel way??
From your description, it sounds to me like you want to use the "scatter" plotting command instead of the "plot" plotting command. This will allow the use of redundant x-values. Sample code:
import numpy as np
import matplotlib.pyplot as plt
# Generate some data that has non-unique x-values
x1 = np.linspace(1,50)
y1 = x1**2
y2 = 2*x1
x3 = np.append(x1,x1)
y3 = np.append(y1,y2)
# Now plot it using the scatter command
# Note that some of the abbreviations that work with plot,
# such as 'ro' for red circles don't work with scatter
plt.scatter(x3,y3,color='red',marker='o')
As I mentioned in the comments, some of the handy "plot" shortcuts don't work with "scatter" so you may want to check the documentation: http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.scatter
If you want to plot y-values for a given x-values, you need to get the index which has same x-values. If you are working with numpy then you can try
import pylab as plt
import numpy as np
x=np.array([1]*5+[2]*5+[3]*5)
y=np.array([1,2,3,4,5]*3)
idx=(x==1) # Get the index where x-values are 1
plt.plot(y[idx],'o-')
plt.show()
If you are working with lists you can get the index by
# Get the index where x-values are 1
idx=[i for i, j in enumerate(x) if j == 1]
just answering own question,found this around when i had posted this question years back :)
def plotter(y1,y2,y1name,y2name):
averageY1=float(sum(y1)/len(y1))
averageY2=float(sum(y2)/len(y2))
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(y1,'b-',linewidth=2.0)
ax1.set_xlabel("SNo")
# Make the y2-axis label and tick labels match the line color.
ax1.set_ylabel(y1name, color='b')
for tl in ax1.get_yticklabels():
tl.set_color('b')
ax1.axis([0,len(y2),0,max(y1)+50])
ax2 = ax1.twinx()
ax2.plot(y2, 'r-')
ax2.axis([0,len(y2),0,max(y2)+50])
ax2.set_ylabel(y2name, color='r')
for tl in ax2.get_yticklabels():
tl.set_color('r')
plt.title(y1name + " vs " + y2name)
#plt.fill_between(y2,1,y1)
plt.grid(True,linestyle='-',color='0.75')
plt.savefig(y1name+"VS"+y2name+".png",dpi=200)
You can use
import numpy as np
import matplotlib.pyplot as plt
x = np.array([1, 1, 1, 2, 2, 2])
y = np.array([1, 2, 1, 5, 6, 7])
fig, ax = plt.subplots()
ax.plot(np.arange(len(x)), y)
ax.set_xticklabels(x)
plt.show()

Categories

Resources