I have read a list of 3D points from a text file. The list looks like follows:
content = ['2.449,14.651,-0.992,', '6.833,13.875,-1.021,', '8.133,17.431,-1.150,', '3.039,13.724,-0.999,', '16.835,9.456,-1.031,', '16.835,9.457,-1.031,', '15.388,5.893,-0.868,', '13.743,25.743,-1.394,', '14.691,24.988,-1.387,', '15.801,25.161,-1.463,', '14.668,23.056,-1.382,', '22.378,20.268,-1.457,', '21.121,17.041,-1.353,', '19.472,13.555,-1.192,', '22.498,20.115,-1.436,', '13.344,-33.672,-0.282,', '13.329,-33.835,-0.279,', '13.147,-30.690,-0.305,', '13.097,-28.407,-0.339,', '13.251,-28.643,-0.366,', '13.527,-25.067,-0.481,', '19.433,-33.137,-0.408,', '19.445,-29.501,-0.345,', '20.592,-28.004,-0.312,', '19.109,-26.512,-0.380,', '18.521,-24.155,-0.519,', '22.837,48.245,-2.201,', '23.269,50.129,-2.282,', '23.499,46.652,-2.297,', '23.814,48.646,-2.271,', '30.377,46.501,-2.214,', '29.869,44.479,-2.143,', '29.597,41.257,-2.018,', '28.134,40.291,-2.159,', '-40.932,-0.320,-1.390,', '-36.808,0.442,-1.382,', '-30.831,0.548,-1.288,', '-29.404,1.235,-1.300,', '-26.453,1.424,-1.261,', '-30.559,2.775,-1.249,', '-27.714,3.439,-1.201,']
I want to plot all the points on a 3D plot. I have this so far:
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
with open("measurements.txt") as f:
content = f.read().splitlines()
#print content
for value in content:
x, y, z = value.split(',')
#print x, y, z
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.scatter(x, y, z)
fig.savefig('scatterplot.png')
It throws an error:
Traceback (most recent call last): File "plotting.py", line 11, in
x, y, z = value.split(',')
ValueError: too many values to unpack
How do I plot these points? Thank you for your help.
First of all you need to take the values into respective arrays by spitting lines in file then pass them to the function.
content = ['2.449,14.651,-0.992,', '6.833,13.875,-1.021,', '8.133,17.431,-1.150,', '3.039,13.724,-0.999,', '16.835,9.456,-1.031,', '16.835,9.457,-1.031,', '15.388,5.893,-0.868,', '13.743,25.743,-1.394,', '14.691,24.988,-1.387,', '15.801,25.161,-1.463,', '14.668,23.056,-1.382,', '22.378,20.268,-1.457,', '21.121,17.041,-1.353,', '19.472,13.555,-1.192,', '22.498,20.115,-1.436,', '13.344,-33.672,-0.282,', '13.329,-33.835,-0.279,', '13.147,-30.690,-0.305,', '13.097,-28.407,-0.339,', '13.251,-28.643,-0.366,', '13.527,-25.067,-0.481,', '19.433,-33.137,-0.408,', '19.445,-29.501,-0.345,', '20.592,-28.004,-0.312,', '19.109,-26.512,-0.380,', '18.521,-24.155,-0.519,', '22.837,48.245,-2.201,', '23.269,50.129,-2.282,', '23.499,46.652,-2.297,', '23.814,48.646,-2.271,', '30.377,46.501,-2.214,', '29.869,44.479,-2.143,', '29.597,41.257,-2.018,', '28.134,40.291,-2.159,', '-40.932,-0.320,-1.390,', '-36.808,0.442,-1.382,', '-30.831,0.548,-1.288,', '-29.404,1.235,-1.300,', '-26.453,1.424,-1.261,', '-30.559,2.775,-1.249,', '-27.714,3.439,-1.201,']
import numpy as np
import matplotlib.pyplot as plt
#with open("measurements.txt") as f:
#content = f.read().splitlines()
#print content
#for value in content:
# x, y, z = value.split(',')
x = [float(i.split(',')[0]) for i in content]
y = [float(i.split(',')[1]) for i in content]
z = [float(i.split(',')[2]) for i in content]
#print(x, y, z)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.scatter(x, y, z)
fig.savefig('scatterplot.png')
output
It's clear ! when you do your split there is 4 values
content = ['2.449,14.651,-0.992,', '6.833,13.875,-1.021,', '8.133,17.431,-1.150,', '3.039,13.724,-0.999,', '16.835,9.456,-1.031,', '16.835,9.457,-1.031,', '15.388,5.893,-0.868,', '13.743,25.743,-1.394,', '14.691,24.988,-1.387,', '15.801,25.161,-1.463,', '14.668,23.056,-1.382,', '22.378,20.268,-1.457,', '21.121,17.041,-1.353,', '19.472,13.555,-1.192,', '22.498,20.115,-1.436,', '13.344,-33.672,-0.282,', '13.329,-33.835,-0.279,', '13.147,-30.690,-0.305,', '13.097,-28.407,-0.339,', '13.251,-28.643,-0.366,', '13.527,-25.067,-0.481,', '19.433,-33.137,-0.408,', '19.445,-29.501,-0.345,', '20.592,-28.004,-0.312,', '19.109,-26.512,-0.380,', '18.521,-24.155,-0.519,', '22.837,48.245,-2.201,', '23.269,50.129,-2.282,', '23.499,46.652,-2.297,', '23.814,48.646,-2.271,', '30.377,46.501,-2.214,', '29.869,44.479,-2.143,', '29.597,41.257,-2.018,', '28.134,40.291,-2.159,', '-40.932,-0.320,-1.390,', '-36.808,0.442,-1.382,', '-30.831,0.548,-1.288,', '-29.404,1.235,-1.300,', '-26.453,1.424,-1.261,', '-30.559,2.775,-1.249,', '-27.714,3.439,-1.201,']
Solution:
for value in content:
x, y, z,parasitic_value = value.split(',')
The element in content are:
'2.449,14.651,-0.992,'
A slightly different way to extract the data to plot from this string is to consider it as a tuple, and to use eval().
data = [eval("("+x[:len(x)-1]+")") for x in content]
Which returns:
[(2.449, 14.651, -0.992),
(6.833, 13.875, -1.021),
(8.133, 17.431, -1.15),
...
(-30.559, 2.775, -1.249),
(-27.714, 3.439, -1.201)]
EDIT: the error you got means:
You want 3 values, X, Y and Z; but when I split at ",", There are more (too many values to unpack).
content[0].split(",")
Out[4]: ['2.449', '14.651', '-0.992', '']
I see at least one error in there.
The most obvious one (because you got an error), is in splitting.
The third comma at the end is causing the string to be split into four elements
>>> l = 'a,b,c,'
>>> l.split(',')
['a', 'b', 'c', '']
you can work around that by using:
x,y,z,_ = value.split(',')
the next problem you'll run into is with your loop
for value in content:
x, y, z = value.split(',')
you are only storing the last of your values, since you overwrite them multiple times.
The easiest way to work around this is creating three lists and appending into them:
x = []
y = []
z = []
for measurement in content:
a,b,c,_ = measurement.split(',')
x.append(a)
y.append(b)
z.append(c)
This is not the most efficient way, but I think it should be easier to understand.
I recommend using it like this:
x = []
y = []
z = []
with open('measurements.txt') as file:
for line in file:
a,b,c,_ = line.split(',')
x.append(a)
y.append(b)
z.append(c)
To solve the main issue , you have to edit the list , and make it a 3d numpy array , by copying all the values , traversing the list via re.
Rather than assuming the list as multiple points , try to take the first 2 points or 3 points as a image/3D graph , and use imshow or Axes3D to plot it.
Related
here is example of one txt file
My experiment measurements are in several txt files (in reality I will have hundreds of files, but to demonstrate the idea of plotting, here I only list out 3 files, they are d_401.txt, d_402.txt, d_403.txt) each file has 4 columns & 256 rows of data. (only the first & forth column are the data I need for x and z)
I want to plot a 3D surface plot/or contour plot out of these files. In My 3D plot, x-axis is universally the 1st column data from each file, z-axis is the 4th column data from each file (z value also needs to be color-coded in gradient), and finally y-axis is "line-up in y direction" of all the x-z values plot from these three files.
How to generate the python code for this plot? I'm especially confused how to assign the matrix Z, would greatly appreciate if somebody could help me on this issue...I need to have the figure plotted soon.
I attached my pre-mature (supposedly full of error code)
Thanks a million!!!
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
z_txt = np.array(['d_401.txt', 'd_402.txt', 'd_403.txt'])
zarray = np.zeros([z_txt.size])
y = np.arange(3)
x = np.zeros(256)
Z = np.zeros((len(y),len(x)))
for i in range(z_txt.size):
zarray[i] = np.loadtxt(z_txt[i])
x[i] = zarray[i,0]
X, Y = np.meshgrid(x,y)
Z[i] = zarray[i,3]
ax.plot_surface(X, Y, Z, cmap=cm.magma, shade=True, lw=3)
plt.show()
The following in the hypotesis that all the files contain the same vector of x …
In [146]: import numpy as np
...: import matplotlib.pyplot as plt
...: from matplotlib.cm import ScalarMappable as sm
...: from glob import glob
...:
...: # create fake data and put it in files
...: x0, y0 = np.arange(11.0), np.arange(3)+1.0
...: z0 = x0+y0[:,None]
...: for i in range(3):
...: with open('delendo%d'%(i+1), 'w') as out:
...: for x0z0 in zip(x0, x0+x0, x0-x0, z0[i]):
...: print(*x0z0, sep='\t', file=out)
...:
...: # sorted list of "interesting" files
...: files = sorted(glob('delendo*'))
...:
...: # read the matrix of z values
...: Z = np.array([np.loadtxt(f, usecols=3) for f in files])
...: # read the vector of x values (the same for all files, I hope so!)
...: x = np.loadtxt(files[0], usecols=0)
...: # we have as many y's as rows in Z, and we count from 1
...: y = np.arange(Z.shape[0])+1
...:
...: # prepare for a 3D plot and plot as a surface
...: fig, ax = plt.subplots(constrained_layout=1,
...: subplot_kw={"projection" : "3d"})
...: surf = ax.plot_surface(*np.meshgrid(x,y), Z, cmap='magma')
...: norm = plt.Normalize(*surf.get_clim())
...: plt.colorbar(sm(norm=norm, cmap='magma'))
...: plt.show()
Addendum
to address some questions raised in comment from OP
You ask about the *sequence operator. It is the unpack operator, that can be used either in an assignment or in an expression. Let's see
>>> tup = (1,2,3,4,5)
>>> a = tup ; print(a)
(1, 2, 3, 4, 5)
>>> a, b = tup
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
But if you use the unpack operator
>>> a, *b = tup ; print(a, b)
1 [2, 3, 4, 5]
Python understand that b is a sequence and stores in this sequence the remaining elements of tup — incidentally, b is a list not a tuple.
The unpack operator can be used in the middle of a left member, but just once because Python assign to the starred item what is left, and two starred items lead to ambiguity.
>>> a,*b, c = tup ; print(a, b, c)
1 [2, 3, 4] 5
>>> a,*b, *c = tup ; print(a, b, c)
File "<stdin>", line 1
SyntaxError: multiple starred expressions in assignment
Now, let's see the unpack operator at work in an expression
>>> print(*b)
2 3 4
using the starred syntax the list is unpacked, it's like
>>> print(b[0], b[1], b[2])
2 3 4
Now, coming to your question, you have already used unpacking in your code. even if you possibly was not aware of it…
X, Y = np.meshgrid(x,y)
Coming to my code
surf = ax.plot_surface(*np.meshgrid(x,y), Z, cmap='magma')
at this point it should be clear, we are unpacking what is returned by meshgrid, it's like X, Y = meshgrid(...) ; surf = ...(X, Y, Z, ...) with the (small) advantage that the grid arrays (X, Y) are not referenced and the memory they use can be immediately given back to the interpreter.
With respect to your last point, to create a colorbar Matplotlib needs what is called a scalar mappable (s.m.) and plt.colorbar can check the active Axes to see if there is a s.m. hanging around.
Many Artists (the software agents that draw into the Axes) like e.g., imshow create a s.m. and all is well, but plot_surface won't, so the poor programmer has to provide a hand-made s.m. to colorbar.
To specify a s.m. we need ① a norm and ② a colormap.
The norm is the default, i.e., plt.Normalize, we need the limits of Z and these are the value of surf.get_clim(), again I used the starred syntax to unpack the two values.
The length of this explanation is a justification for having it omitted in the first place…
So I made my list but after that I don't know how to take out of it my x and y so I can use it later to create a graph
import random
import numpy as np
import matplotlib.pyplot as plt
tabuletson = []
for i in range(0, 10):
x = round(random.uniform(-1000,1000),2)
y = (2*x+1)
tabuletson.append([x,y])
print(tabuletson)
wielomian = np.poly1d(np.polyfit(x,y,3))
linia = np.linspace(-2000,2000,2000)
plt.scatter(x,y)
plt.plot(linia,wielomian(linia))
plt.show()
All you have to do is to add one line of code after and outside your for loop. This command will create two lists containing x and y values. You can use the same variable names x and y.
x, y = zip(*tabuletson)
I think that this is a better way to do what you want according of how plt.scatter and plt.plot work. Hope it works as you want!
import random
import numpy as np
import matplotlib.pyplot as plt
x = []; y = []
for i in range(10):
x.append(round(random.uniform(-1000,1000),2))
y.append(2*x[i]+1)
wielomian = np.poly1d(np.polyfit(x,y,3))
linia = np.linspace(-2000,2000,2000)
plt.scatter(x,y)
plt.plot(linia,wielomian(linia))
plt.show()
The np.polyfit and plt.scatter functions you are using require separate lists of X and Y coordinates.
Try:
import random
import numpy as np
import matplotlib.pyplot as plt
tabuletson_x = []
tabuletson_y = []
for i in range(0, 10):
x = round(random.uniform(-1000,1000),2)
y = (2*x+1)
tabuletson_x.append(x)
tabuletson_y.append(y)
print(tabuletson_x)
print(tabuletson_y)
wielomian = np.poly1d(np.polyfit(tabuletson_x,tabuletson_y,3))
linia = np.linspace(-2000,2000,2000)
plt.scatter(tabuletson_x,tabuletson_y)
plt.plot(linia,wielomian(linia))
plt.show()
Note: referencing x and y after the for cycle will give you the last values from the randomly generated list:
list of x vals: [-8.78, 554.81, -693.22, 955.8, 88.95, 235.55, -108.67, -804.08, 494.65, 754.58]
list of y vals: [-16.56, 1110.62, -1385.44, 1912.6, 178.9, 472.1, -216.34, -1607.16, 990.3, 1510.16]
x: 754.58
y: 1510.16
For more info:
PyPlot Scatter documentation
PolyFit documentation
Your x and y are stored in your list tabuletson. Like this: [[x0,y0], [x1,y1], ..., [x,y]]
So you can, for example, get the value of x1 and y1 with x1 = tabuletson[1][0] and y1 = tabuletson[1][1]
Is that your question ?
tabuletson = np.array(tabuletson)
X, Y = tabuletson[:,0], tabuletson[:,1]
X will have all your xs from list
And, Y will have all your ys from list
I am trying to plot a .dat file from an stellar catalog using this code
try:
import pyfits
noPyfits=False
except:
noPyfits=True
import matplotlib.pyplot as plt
import numpy as np
f2 = open('/home/mcditoos/Desktop/Astrophysics_programs/Data_LAFT/ESPECTROS/165401.dat', 'r')
lines = f2.readlines()
f2.close()
x1 = []
y1 = []
for line in lines:
p = line.split()
x1.append(float(p[0]))
y1.append(float(p[1]))
xv = np.array(x1)
yv = np.array(y1)
plt.plot(xv, yv)
plt.show()
however i get the following error:
x1.append(float(p[0]))
IndexError: list index out of range
also i wanted to know if there is anyway of making it a program capable of opening the next .dat file given an input
I may not understand fully your question but why don't you use
X, Y = numpy.genfromtxt('yourfile', dtype='str')
X = X.astype('float')
Y = Y.astype('float')
If in your file you have 2 columns you can transpose your table with
X, Y = numpy.genfromtxt('yourfile', dtype='str').T
I have some data I want to plot, x and y is in the same format as this small piece of example code.
import matplotlib.pyplot as plt
y = [1,1,3,4]
x = [1,4,2,3]
plt.plot(x,y,'-o')
plt.show()
This results in quite a weird graph.
What pyplot does is drawing a line from the first point inserted to the second, then to the third etc.
I want it to draw a line from low-x to high-x, but I can seem to find a nice way to do this. I want my line to be like this.
What is the easiest way to achieve this, given my x and y data is in the same format but more complex than this example?
To get the graph as you mentioned, you need to have values in x in sorted order, which you can achieve like this:
z = sorted(zip(x,y))
x=[i[0] for i in z]
y=[i[1] for i in z]
and now using x and y for ploting (not tested).
you can sort your x list with simultaneously changing the y,
import matplotlib.pyplot as plt
y = [1,1,3,4]
x = [1,4,2,3]
for i in range(len(x)):
for k in range( len( x ) - 1, i, -1 ):
if ( x[k] < x[k - 1] ):
x[k-1],x[k]=x[k],x[k-1]
y[k-1],y[k]= y[k],y[k-1]
print x,y
plt.plot(x,y,'-o')
plt.show()
I am trying to plot a graph something similar to this:
For that, I have written the following function in python
def plot_graph_perf(dataset):
#TODO: Give labels as power ranges in spaces of 1000
plotter = ['0',
'1200000-10', '1200000-14', '1200000-18',
'1200000-2', '1200000-22', '1200000-26', '1200000-30',
'1200000-34', '1200000-38', '1200000-42', '1200000-46',
'1200000-6',
'1600000-10', '1600000-14',
'1600000-18', '1600000-2', '1600000-22',
'1600000-26', '1600000-30', '1600000-34',
'1600000-38', '1600000-42', '1600000-46',
'1600000-6',
'2000000-10', '2000000-14',
'2000000-18', '2000000-2', '2000000-22',
'2000000-26', '2000000-30', '2000000-34',
'2000000-38', '2000000-42', '2000000-46',
'2000000-6',
'2400000-10', '2400000-14',
'2400000-18', '2400000-2', '2400000-22',
'2400000-26', '2400000-30', '2400000-34',
'2400000-38', '2400000-42', '2400000-46',
'2400000-6' ,
'800000-10', '800000-14',
'800000-18', '800000-2', '800000-22',
'800000-26', '800000-30', '800000-34',
'800000-38', '800000-42', '800000-46',
'800000-6' ]
x_axis_labels = dataset[1]
x=[a for a in range(len(x_axis_labels))]
y_axis_labels = dataset[0]
y=[a for a in range(len(y_axis_labels))]
width = 0.1
plt.figure
plt.plot(plotter, color = 'g')
plt.tight_layout(pad=1, h_pad=4, w_pad=None)
plt.xticks(x,x_axis_labels, rotation='vertical')
plt.yticks(y,y_axis_labels, rotation='horizontal')
plt.xlabel('Power')
plt.ylabel('perf')
plt.title(file + ' | (Power)')
fig = plt.gcf()
fig.set_size_inches(28.5,10.5)
plt.savefig('watt' + '.png',bbox_inches='tight', pad_inches=0.5,dpi=100)
plt.clf()
Where dataset is two dimensional list something like this
dataset = [[],[]]
each sublist containing same number of elements as plotter.
I plotted dataset[0] and dataset[1] as y and x respectively, but was unable to plot the string values in plotter.
Can you please shed some light and help me plot the plotter values on the graph.
Thanks.
You have to call the text function for each word separately:
words = list("abcdefg")
xs = np.random.randint(0,10,len(words))
ys = np.random.randint(0,10,len(words))
for x, y, s in zip(xs,ys,words):
plt.text(x,y,s)