I have a text file that consists of 3 columns.
column contain X coordinate
column contain Y coordinate
column contain 0 or 1
So far I draw all the coordinates:
import matplotlib.pyplot as plt
import numpy as np
x, y = np.loadtxt("coordinates.txt",delimiter=' ',skiprows=1, usecols=(0,1),unpack=True)
plt.plot(x,y)
plt.show()
I want to draw only those coordinates where the value of 2rd column is 1.
Please help me.
hope this help:
import matplotlib.pyplot as plt
import numpy as np
f = np.loadtxt('coordinates.txt',delimiter=' ',skiprows=1)
f = f[f[:,2] == 1]
x = f[:,0]
y = f[:,1]
plt.plot([x], [y], 'ro')
plt.show()
The long way to do this is using a loop that plots (lets say) dots based on position within the list. But it might be a helpful to you, considering your comments.
Based on your comments, the data you're dealing with is considered as a string. Be sure to check types of data if you're planing to deal with programming. https://www.tutorialspoint.com/python/python_variable_types.htm
import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt('coordinates.txt',delimiter=' ',skiprows=1)
x_data = data[:,0] # [every row, "1st" column]
y_data = data[:,1] # [every row, "2nd" column]
z_data = data[:,2] # [every row, "3rd" column]
#check every number in z and if it is equal to your desired condition,
#plot blue circle ('bo') on coordinates where that condition is satisfied (x[i], y[i])
for i in range(len(z)):
if z[i] == str(1):
plt.plot(x[i],y[i], 'bo')
You can also plot every dot, and make them different like this:
for i in range(len(z)):
if z[i] == str(1):
plt.plot(x[i],y[i], 'bo') #ones are blue dots
else:
plt.plot(x[i],y[i], 'ro') #zeros are red dots
I would definitely recommend that you do some research on how to read data and how to deal with it when it's read (for example: converting strings to floats), because this is not the proper way to do this, but it will do the trick.
Related
I am struggling a bit with the pandas transformations needed to make data render in 3D on matplot lib. The data I have is usually in columns of numbers (usually time and some value). So lets create some test data to illustrate.
import pandas as pd
pattern = ("....1...."
"....1...."
"..11111.."
".1133311."
"111393111"
".1133311."
"..11111.."
"....1...."
"....1....")
# create the data and coords
Zdata = list(map(lambda d:0 if d == '.' else int(d), pattern))
Zinverse = list(map(lambda d:1 if d == '.' else -int(d), pattern))
Xdata = [x for y in range(1,10) for x in range(1,10)]
Ydata = [y for y in range(1,10) for x in range(1,10)]
# pivot the data into columns
data = [d for d in zip(Xdata,Ydata,Zdata,Zinverse)]
# create the data frame
df = pd.DataFrame(data, columns=['X','Y','Z',"Zi"], index=zip(Xdata,Ydata))
df.head(5)
Edit: This block of data is demo data that would normally come from a query on a
database that may need more cleaning and transforms before plotting. In this case data is already aligned and there are no problems aside having one more column we don't need (Zi).
So the numbers in pattern are transferred into height data in the Z column of df ('Zi' being the inverse image) and with that as the data frame I've struggled to come up with this pivot method which is 3 separate operations. I wonder if that can be better.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xs = df.pivot(index='X', columns='Y', values='X').values
Ys = df.pivot(index='X', columns='Y', values='Y').values
Zs = df.pivot(index='X', columns='Y', values='Z').values
ax.plot_surface(Xs,Ys,Zs, cmap=cm.RdYlGn)
plt.show()
Although I have something working I feel there must be a better way than what I'm doing. On a big data set I would imagine doing 3 pivots is an expensive way to plot something. Is there a more efficient way to transform this data ?
I guess you can avoid some steps during the preparation of the data by not using pandas (but only numpy arrays) and by using some convenience fonctions provided by numpy such as linespace and meshgrid.
I rewrote your code to do so, trying to keep the same logic and the same variable names :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
pattern = ("....1...."
"....1...."
"..11111.."
".1133311."
"111393111"
".1133311."
"..11111.."
"....1...."
"....1....")
# Extract the value according to your logic
Zdata = list(map(lambda d:0 if d == '.' else int(d), pattern))
# Assuming the pattern is always a square
size = int(len(Zdata) ** 0.5)
# Create a mesh grid for plotting the surface
Xdata = np.linspace(1, size, size)
Ydata = np.linspace(1, size, size)
Xs, Ys = np.meshgrid(Xdata, Ydata)
# Convert the Zdata to a numpy array with the appropriate shape
Zs = np.array(Zdata).reshape((size, size))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Plot the surface
ax.plot_surface(Xs, Ys, Zs, cmap=cm.RdYlGn)
plt.show()
I saw a python graph that looks like the following:
I think doing something like this really puts emphasis on certain data points and takes away a lot of clutter. Using the adjust text library, I know how to label points with the following code:
from adjustText import adjust_text
texts = [plt.text(x0,y0,name,ha='right',va='bottom') for x0,y0,name in zip(
df.x, df1.y, df1.label)]
adjust_text(texts)
What could I add to this code to only label points that are, say, greater than 5?
Also, how could I go about coloring all data points outside of that threshold (less than 5) gray, as seen in the picture?
I've been reading documentation to no avail, so I decided to ask you all here. Thanks in advance!
EDIT: I am using a dictionary to color the points, so I'm good there. I just would like to know how to convert data points that don't meet a requirement back to gray
Here's my code for coloring the points:
for i in range(len(df)):
ax.scatter(df.x.iloc[i], df.y.iloc[i],alpha=.7,color=COLORS[df.color.iloc[i]])
Calling scatter for each point isn't the most efficient. You can call scatter twice: once for data below and once for above the threshold:
threshold = 5
ix = df.y < threshold
ax.scatter(df.x[ix], df.y[ix], c='gray')
ax.scatter(df.x[~ix], df.y[~ix], c=COLORS[df.color[~ix]]
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
colors = list('rgbcmyk')
color_cycle = cycle(colors)
np.random.seed(42)
n = 100
df = pd.DataFrame(np.random.random((n, 2)), columns=['x', 'y'])
df['colors'] = [c for c, _ in zip(color_cycle, range(n))]
ix = df.y < 0.75
fig, ax = plt.subplots()
ax.scatter(df.x[ix], df.y[ix], c='gray')
ax.scatter(df.x[~ix], df.y[~ix], c=df.colors[~ix])
I have a sequence of data files which contain two columns of data (x value, and z value). I want to asign each file with a unique constant y value with a loop and then use x,y,z values to make a contour plot.
import glob
import matplotlib.pyplot as plt
import numpy as np
files=glob.glob('C:\Users\DDT\Desktop\DATA TIANYU\materials\AB2O4\synchronchron\OX1\YbFe1Mn1O4_2cyc_600_meth_ox1-*.xye')
s1=1
for file in files:
t1=s1/3
x,z = np.loadtxt(file,skiprows=3,unpack=True, usecols=[0,1])
def f(x, y):
return x*0 +y*0 +z
l1=np.size(x)
y=np.full(l1, t1,dtype=int)
X,Y=np.meshgrid(x,y)
Z = f(X,Y)
plt.contour(X,Y,Z)
s1=s1+1
continue
plt.show()
There is no error in this code, however what I got is an empty figure with nothing.
What mistake did I make?
It is very hard to guess what you're trying to do. Here is an attempt. It supposes that all x-arrays are equal. And that the y really makes sense (although that is hard if the files are read in an unspecified order). To get a useful plot, the data from all the files should be collected before starting to plot.
import glob
import matplotlib.pyplot as plt
import numpy as np
files = glob.glob('........')
zs = []
for file in files:
x, z = np.loadtxt(file, skiprows=3, unpack=True, usecols=[0, 1])
zs.append(z)
# without creating a new x, the x from the last file will be used
# x = np.linspace(0, 15, 10)
y = np.linspace(-100, 1000, len(zs))
zs = np.array(zs)
fig, axs = plt.subplots(ncols=2)
axs[0].scatter(np.tile(x, y.size), np.repeat(y, x.size), c=zs)
axs[1].contour(x, y, zs)
plt.show()
With simulated random data, the scatter plot and the contour plot would look like:
I have a list containing array elements:
[array([2.40460915, 0.85513601]), array([1.80998096, 0.97406986]), array([2.14505475, 0.96109123]),
array([2.12467111, 0.93991277])]
And I want to plot that list using mathplotlib, such that i iterate over each element in the list, and plot the ith element, using plt.scatter(x,y) where x is the first element of the array at the ith position, and similar for y the second element.
I am not super familiar with how to do this indexing in python, and no matter how I try to solve this, I cannot get a plot.
for i in range(len(list)):
# plt.scatter(x,y) for x,y as described above
Can anyone tell me an easy way to do this?
from numpy import array
import matplotlib.pyplot as plt
a = [array([2.40460915, 0.85513601]), array([1.80998096, 0.97406986]), array([2.14505475, 0.96109123]),
array([2.12467111, 0.93991277])]
# *i unpacks i into a tuple (i[0], i[1]), which is interpreted as (x,y) by plt.scatter
for i in a:
plt.scatter(*i)
plt.show()
You can zip the unpacked values of numpy array a.
One-liner to plot as you want:
plt.scatter(*zip(*a))
which is equivalent to x,y=zip(*a); plt.scatter(x,y)
import numpy as np
import matplotlib.pyplot as plt
a=[np.array([2.40460915, 0.85513601]), np.array([1.80998096, 0.97406986]), np.array([2.14505475, 0.96109123]), np.array([2.12467111, 0.93991277])]
plt.scatter(*zip(*a)) #x,y=zip(*a)
plt.show()
This would do it:
import matplotlib.pyplot as plt
import numpy as np
a= [np.array([2.40460915, 0.85513601]),
np.array([1.80998096, 0.97406986]),
np.array([2.14505475, 0.96109123]),
np.array([2.12467111, 0.93991277])]
plt.scatter([i[0] for i in a], [i[1] for i in a]) # just this line here
plt.show()
There are many solutions to this question. I write two that you will understand easily:
Solution 1: many scatters
for i in range(len(data)):
point = data[i] #the element ith in data
x = point[0] #the first coordenate of the point, x
y = point[1] #the second coordenate of the point, y
plt.scatter(x,y) #plot the point
plt.show()
Solution 2: one scatter (I recomend if you are not familiarizated with indexing)
x = []
y = []
for i in range(len(data)):
point = data[i]
x.append(point[0])
y.append(point[1])
plt.scatter(x,y)
plt.show()
try converting the array into pandas Dataframe by
data=pd.DataFrame(data='''array''')
and try plotting the datas
Here is my resulting plot below but I would like it to look like the truncated dendrograms in astrodendro such as this:
There is also a really cool looking dendrogram from this paper that I would like to recreate in matplotlib.
Below is the code for generating an iris data set with noise variables and plotting the dendrogram in matplotlib.
Does anyone know how to either: (1) truncate the branches like in the example figures; and/or (2) to use astrodendro with a custom linkage matrix and labels?
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import astrodendro
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial import distance
def iris_data(noise=None, palette="hls", desat=1):
# Iris dataset
X = pd.DataFrame(load_iris().data,
index = [*map(lambda x:f"iris_{x}", range(150))],
columns = [*map(lambda x: x.split(" (cm)")[0].replace(" ","_"), load_iris().feature_names)])
y = pd.Series(load_iris().target,
index = X.index,
name = "Species")
c = map_colors(y, mode=1, palette=palette, desat=desat)#y.map(lambda x:{0:"red",1:"green",2:"blue"}[x])
if noise is not None:
X_noise = pd.DataFrame(
np.random.RandomState(0).normal(size=(X.shape[0], noise)),
index=X_iris.index,
columns=[*map(lambda x:f"noise_{x}", range(noise))]
)
X = pd.concat([X, X_noise], axis=1)
return (X, y, c)
def dism2linkage(DF_dism, method="ward"):
"""
Input: A (m x m) dissimalrity Pandas DataFrame object where the diagonal is 0
Output: Hierarchical clustering encoded as a linkage matrix
Further reading:
http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.cluster.hierarchy.linkage.html
https://pypi.python.org/pypi/fastcluster
"""
#Linkage Matrix
Ar_dist = distance.squareform(DF_dism.as_matrix())
return linkage(Ar_dist,method=method)
# Get data
X_iris_with_noise, y_iris, c_iris = iris_data(50)
# Get distance matrix
df_dism = 1- X_iris_with_noise.corr().abs()
# Get linkage matrix
Z = dism2linkage(df_dism)
#Create dendrogram
with plt.style.context("seaborn-white"):
fig, ax = plt.subplots(figsize=(13,3))
D_dendro = dendrogram(
Z,
labels=df_dism.index,
color_threshold=3.5,
count_sort = "ascending",
#link_color_func=lambda k: colors[k]
ax=ax
)
ax.set_ylabel("Distance")
I'm not sure this really constitutes a practical answer, but it does allow you to generate dendrograms with truncated hanging lines. The trick is to generate the plot as normal, then manipulate the resulting matplotlib plot to recreate the lines.
I couldn't get your example to work locally, so I've just created a dummy dataset.
from matplotlib import pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage
import numpy as np
a = np.random.multivariate_normal([0, 10], [[3, 1], [1, 4]], size=[5,])
b = np.random.multivariate_normal([0, 10], [[3, 1], [1, 4]], size=[5,])
X = np.concatenate((a, b),)
Z = linkage(X, 'ward')
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
dendrogram(Z, ax=ax)
The resulting plot is the usual long-arm dendrogram.
Now for the more interesting bit. A dendrogram is made up of a number of LineCollection objects (one for each colour). To update the lines we iterate through these, extracting the details about their constituent paths, modifying these to remove any lines reaching to a y of zero, and then recreating a LineCollection for these modified paths.
The updated path is then added to the axes, and the original is removed.
The one tricky part is determining what height to draw to instead of zero. Since we are iterating over each dendrograms path, we don't know which point came before — we basically have no idea where we are. However, we can exploit the fact that hanging lines hang vertically. Assuming there are no lines on the same x, we can look for the known other y values for a given x and use that as the basis for our new y when calculating. The downside is that in order to make sure we have this number, we have to pre-scan the data.
Note: If you can get dendrogram hanging lines on the same x, you would need to include the y and search for nearest y above this x to do this.
import numpy as np
from matplotlib.path import Path
from matplotlib.collections import LineCollection
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
dendrogram(Z, ax=ax);
for c in ax.collections[:]: # use [:] to get a copy, since we're adding to the same list
paths = []
for path in c.get_paths():
segments = []
y_at_x = {}
# Pre-pass over all elements, to find the lowest y value at each x value.
# we can use this to caculate where to cut our lines.
for n, seg in enumerate(path.iter_segments()):
x, y = seg[0]
# Don't store if the y is zero, or if it's higher than the current low.
if y > 0 and y < y_at_x.get(x, np.inf):
y_at_x[x] = y
for n, seg in enumerate(path.iter_segments()):
x, y = seg[0]
if y == 0:
# If we know the last y at this x, use it - 0.5, limit > 0
y = max(0, y_at_x.get(x, 0) - 0.5)
segments.append([x,y])
paths.append(segments)
lc = LineCollection(paths, colors=c.get_colors()) # Recreate a LineCollection with the same params
ax.add_collection(lc)
ax.collections.remove(c) # Remove the original LineCollection
The resulting dendrogram looks like this: