visualize a two-dimensional point set using Python - python
I'm new to Python and want to perform a rather simple task. I've got a two-dimensional point set, which is stored as binary data (i.e. (x, y)-coordinates) in a file, which I want to visualize. The output should look as in the picture below.
However, I'm somehow overwhelmed by the amount of google results on this topic. And many of them seem to be for three-dimensional point cloud visualization and/or a massive amount of data points. So, if anyone could point me to a suitable solution for my problem, I would be really thankful.
EDIT: The point set is contained in a file which is formatted as follows:
0.000000000000000 0.000000000000000
1.000000000000000 1.000000000000000
1
0.020375738732779 0.026169010160356
0.050815740313746 0.023209931647163
0.072530406907906 0.023975230642589
The first data vector is the one in the line below the single "1"; i.e. (0.020375738732779, 0.026169010160356). How do I read this into a vector in python? I can open the file using f = open("pointset file")
Install and import matplotlib and pyplot:
import matplotlib.pyplot as plt
Assuming this is your data:
x = [1, 2, 5, 1, 5, 7, 8, 3, 2, 6]
y = [6, 7, 1, 2, 6, 2, 1, 6, 3, 1]
If you need, you can use a comprehension to split the coordinates into seperate lists:
x = [p[0] for p in points]
y = [p[1] for p in points]
Plotting is as simple as:
plt.scatter(x=x, y=y)
Result:
Many customizations are possible.
EDIT: following question edit
In order to read the file:
x = []
y = []
with open('pointset_file.txt', 'r') as f:
for line in f:
coords = line.split(' ')
x.append(float(coords[0]))
y.append(float(coords[1]))
You could read your data as follow, and plot using scattr plot. this method is considering for small number of data and not csv, just the format you have presented.
import matplotlib.pyplot as plt
with open("pointset file") as fid:
lines = fid.read().split("\n")
# lines[:2] looks like the bounds for each axis, if yes use it in plot
data = [[float(d) for d in line.split(" ") if d] for line in lines[3:]]
plt.scatter(data[0], data[1])
plt.show()
Assuming you want a plot looking pretty much exactly like the sample image you give, and you want the plot to display the data with both axes in equal proportion, one could use a general purpose multimedia library like pygame to achieve this:
#!/usr/bin/env python3
import sys
import pygame
# windows will never be larger than this in their largest dimension
MAX_WINDOW_SIZE = 400
BG_COLOUR = (255, 255, 255,)
FG_COLOUR = (0, 0, 0,)
DATA_POINT_SIZE = 2
pygame.init()
if len(sys.argv) < 2:
print('Error: need filename to read data from')
pygame.quit()
sys.exit(1)
else:
data_points = []
# read in data points from file first
with open(sys.argv[1], 'r') as file:
[next(file) for _ in range(3)] # discard first 3 lines of file
# now the rest of the file contains actual data to process
data_points.extend(tuple(float(x) for x in line.split()) for line in file)
# file read complete. now let's find the min and max bounds of the data
top_left = [float('+Inf'), float('+Inf')]
bottom_right = [float('-Inf'), float('-Inf')]
for datum in data_points:
if datum[0] < top_left[0]:
top_left[0] = datum[0]
if datum[1] < top_left[1]:
top_left[1] = datum[1]
if datum[0] > bottom_right[0]:
bottom_right[0] = datum[0]
if datum[1] > bottom_right[1]:
bottom_right[1] = datum[1]
# calculate space dimensions
space_dimensions = (bottom_right[0] - top_left[0], bottom_right[1] - top_left[1])
# take the biggest of the X or Y dimensions of the point space and scale it
# up to our maximum window size
biggest = max(space_dimensions)
scale_factor = MAX_WINDOW_SIZE / biggest # all points will be scaled up by this factor
# screen dimensions
screen_dimensions = tuple(sd * scale_factor for sd in space_dimensions)
# basic init and draw all points to screen
display = pygame.display.set_mode(screen_dimensions)
display.fill(BG_COLOUR)
for point in data_points:
# translate and scale each point
x = point[0] * scale_factor - top_left[0] * scale_factor
y = point[1] * scale_factor - top_left[1] * scale_factor
pygame.draw.circle(display, FG_COLOUR, (x, y), DATA_POINT_SIZE)
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
pygame.time.wait(50)
Execute this script and pass the name of the file which holds your data in as the first argument. It will spawn a window with the data points displayed.
I generated a bunch of uniformly distributed random x,y points to test it, with:
from random import random
for _ in range(1000):
print(random(), random())
This produces a window looking like the following:
If the space your data points are within is not of square size, the window shape will change to reflect this. The largest dimension of the window, either width or height, will always stay at a specified size (I used 400px as a default in my demo).
Admittedly, this is not the most elegant or concise solution, and reinvents the wheel a little bit, however it gives you the most control on how to display your data points, and it also deals with both the reading in of the file data and the display of it.
To read your file:
import pandas as pd
import numpy as np
df = pd.read_csv('your_file',
sep='\s+',
header=None,
skiprows=3,
names=['x','y'])
For now I've created a random dataset
import random
df = pd.DataFrame({'x':[random.uniform(0, 1) for n in range(100)],
'y':[random.uniform(0, 1) for n in range(100)]})
I prefer Plotly for any kind of figure
import plotly.express as px
fig = px.scatter(df,
x='x',
y='y')
fig.show()
From here you can easily update labels, colors, etc.
Related
Matplotlib - color-coding data plot lines?
I have a python program that reads tsv data and plots it using the matplotlib library. I feel like my code works pretty well: def main(compsPath: str, gibbsPath: str): """ Given the file paths for comps.tsv and gibbs.tsv, this main function will produce two separate plots - one for each file. """ # Read tsv data into np record arrays # Slice off header text with open(compsPath, 'r') as fcomps: reader = csv.reader(fcomps, delimiter='\t') compsHeader = next(reader) compsData = np.array(list(reader)).astype(np.double) with open(gibbsPath, 'r') as fgibbs: reader = csv.reader(fgibbs, delimiter='\t') gibbsHeader = next(reader) gibbsData = np.array(list(reader)).astype(np.double) # Get data dimensions: # - - - M := Number of metabolites # - - - N := Number of reactions M = compsData.shape[1] - 1 N = gibbsData.shape[1] - 1 plotComps(M, compsData, compsHeader) plotGibbs(N, gibbsData, gibbsHeader) plt.show() The plotGibbs function produces the following graphic for the tsv file I'm working with. For this graphic, N=3 (3 reactions). I would like to indicate at what point in time each reaction becomes unfavorable (in the context of my project, this just means that the reaction stops). This occurs when the gibbs free energy value (∆G) of the reaction is greater than or equal to 0. I feel like I could best emphasize this by color-coding the line plots my program generates. For negative ∆G values, I would like the line to be green, and for positive or zero ∆G values, I would like the line to be red. Here is my current code for generating the gibbs free energy plots (does not color-code): def plotGibbs(N: int, gibbsData: np.ndarray, gibbsHeader): gibbsFig = plt.figure() gibbsFig.suptitle("∆G˚ Yield Plotted over Time (days)") numCols = ceil(N / 2) numRows = (N // numCols) + 1 for n in range (1, N+1): ax = gibbsFig.add_subplot(numRows, numCols, n) ax.set_ylabel(gibbsHeader[n]) ax.set_xlabel(gibbsHeader[0]) ax.plot(gibbsData[:, 0], gibbsData[:, n]) gibbsFig.tight_layout() How could I make it so that negative values are plotted green, and non-negative values are plotted red?
You could try to find where a change of sign occurs in your data using np.where with a simple condition like gibbsData[:, n]>0 then plot negative/positive data accordingly: def plotGibbs(N: int, gibbsData: np.ndarray, gibbsHeader): gibbsFig = plt.figure() gibbsFig.suptitle("∆G˚ Yield Plotted over Time (days)") numCols = ceil(N / 2) numRows = (N // numCols) + 1 for n in range (1, N+1): ax = gibbsFig.add_subplot(numRows, numCols, n) ax.set_ylabel(gibbsHeader[n]) ax.set_xlabel(gibbsHeader[0]) # idx where sign change occurs for data n idx_zero = np.where(gibbsData[:, n]>0)[0][0] # negatives y values ax.plot(gibbsData[:idx_zero, 0], gibbsData[:idx_zero,n],'g') # positive y values ax.plot(gibbsData[idx_zero:, 0], gibbsData[idx_zero:,n],'r') gibbsFig.tight_layout()
Filtering 1D numpy arrays in Python
Explanation: I have two numpy arrays: dataX and dataY, and I am trying to filter each array to reduce the noise. The image shown below shows the actual input data (blue dots) and an example of what I want it to be like(red dots). I do not need the filtered data to be as perfect as in the example but I do want it to be as straight as possible. I have provided sample data in the code. What I have tried: Firstly, you can see that the data isn't 'continuous', so I first divided them into individual 'segments' ( 4 of them in this example), and then applied a filter to each 'segment'. Someone suggested that I use a Savitzky-Golay filter. The full, run-able code is below: import scipy as sc import scipy.signal import numpy as np import matplotlib.pyplot as plt # Sample Data ydata = np.array([1,0,1,2,1,2,1,0,1,1,2,2,0,0,1,0,1,0,1,2,7,6,8,6,8,6,6,8,6,6,8,6,6,7,6,5,5,6,6, 10,11,12,13,12,11,10,10,11,10,12,11,10,10,10,10,12,12,10,10,17,16,15,17,16, 17,16,18,19,18,17,16,16,16,16,16,15,16]) xdata = np.array([1,2,3,1,5,4,7,8,6,10,11,12,13,10,12,13,17,16,19,18,21,19,23,21,25,20,26,27,28,26,26,26,29,30,30,29,30,32,33, 1,2,3,1,5,4,7,8,6,10,11,12,13,10,12,13,17,16,19,18,21,19,23,21,25,20,26,27,28,26,26,26,29,30,30,29,30,32]) # Used a diff array to find where there is a big change in Y. # If there's a big change in Y, then there must be a change of 'segment'. diffy = np.diff(ydata) # Create empty numpy arrays to append values into filteredX = np.array([]) filteredY = np.array([]) # Chose 3 to be the value indicating the change in Y index = np.where(diffy >3) # Loop through the array start = 0 for i in range (0, (index[0].size +1) ): # Check if last segment is reached if i == index[0].size: print xdata[start:] partSize = xdata[start:].size # Window length must be an odd integer if partSize % 2 == 0: partSize = partSize - 1 filteredDataX = sc.signal.savgol_filter(xdata[start:], partSize, 3) filteredDataY = sc.signal.savgol_filter(ydata[start:], partSize, 3) filteredX = np.append(filteredX, filteredDataX) filteredY = np.append(filteredY, filteredDataY) else: print xdata[start:index[0][i]] partSize = xdata[start:index[0][i]].size if partSize % 2 == 0: partSize = partSize - 1 filteredDataX = sc.signal.savgol_filter(xdata[start:index[0][i]], partSize, 3) filteredDataY = sc.signal.savgol_filter(ydata[start:index[0][i]], partSize, 3) start = index[0][i] filteredX = np.append(filteredX, filteredDataX) filteredY = np.append(filteredY, filteredDataY) # Plots plt.plot(xdata,ydata, 'bo', label = 'Input Data') plt.plot(filteredX, filteredY, 'ro', label = 'Filtered Data') plt.xlabel('X') plt.ylabel('Y') plt.title('Result') plt.legend() plt.show() This is my result: When each point is connected, the result looks as follows. I have played around with the order, but it seems like a third order gave the best result. I have also tried these filters, among a few others: scipy.signal.medfilt scipy.ndimage.filters.uniform_filter1d But so far none of the filters I have tried were close to what I really wanted. What is the best way to filter data such as this? Looking forward to your help.
One way to get something looking close to your ideal would be clustering + linear regression. Note that you have to provide the number of clusters and I also cheated a bit in scaling up y before clustering. import numpy as np from scipy import cluster, stats ydata = np.array([1,0,1,2,1,2,1,0,1,1,2,2,0,0,1,0,1,0,1,2,7,6,8,6,8,6,6,8,6,6,8,6,6,7,6,5,5,6,6, 10,11,12,13,12,11,10,10,11,10,12,11,10,10,10,10,12,12,10,10,17,16,15,17,16, 17,16,18,19,18,17,16,16,16,16,16,15,16]) xdata = np.array([1,2,3,1,5,4,7,8,6,10,11,12,13,10,12,13,17,16,19,18,21,19,23,21,25,20,26,27,28,26,26,26,29,30,30,29,30,32,33, 1,2,3,1,5,4,7,8,6,10,11,12,13,10,12,13,17,16,19,18,21,19,23,21,25,20,26,27,28,26,26,26,29,30,30,29,30,32]) def split_to_lines(x, y, k): yo = np.empty_like(y, dtype=float) # get the cluster centers and the labels for each point centers, map_ = cluster.vq.kmeans2(np.array((x, y * 2)).T.astype(float), k) # for each cluster, use the labels to select the points belonging to # the cluster and do a linear regression for i in range(k): slope, interc, *_ = stats.linregress(x[map_==i], y[map_==i]) # use the regression parameters to construct y values on the # best fit line yo[map_==i] = x[map_==i] * slope + interc return yo import pylab pylab.plot(xdata, ydata, 'or') pylab.plot(xdata, split_to_lines(xdata, ydata, 4), 'ob') pylab.show()
In matplotlib, how can I plot a multi-colored line, like a rainbow
I would like to plot parallel lines with different colors. E.g. rather than a single red line of thickness 6, I would like to have two parallel lines of thickness 3, with one red and one blue. Any thoughts would be appreciated. Merci Even with the smart offsetting (s. below), there is still an issue in a view that has sharp angles between consecutive points. Zoomed view of smart offsetting: Overlaying lines of varying thickness:
Plotting parallel lines is not an easy task. Using a simple uniform offset will of course not show the desired result. This is shown in the left picture below. Such a simple offset can be produced in matplotlib as shown in the transformation tutorial. Method1 A better solution may be to use the idea sketched on the right side. To calculate the offset of the nth point we can use the normal vector to the line between the n-1st and the n+1st point and use the same distance along this normal vector to calculate the offset point. The advantage of this method is that we have the same number of points in the original line as in the offset line. The disadvantage is that it is not completely accurate, as can be see in the picture. This method is implemented in the function offset in the code below. In order to make this useful for a matplotlib plot, we need to consider that the linewidth should be independent of the data units. Linewidth is usually given in units of points, and the offset would best be given in the same unit, such that e.g. the requirement from the question ("two parallel lines of width 3") can be met. The idea is therefore to transform the coordinates from data to display coordinates, using ax.transData.transform. Also the offset in points o can be transformed to the same units: Using the dpi and the standard of ppi=72, the offset in display coordinates is o*dpi/ppi. After the offset in display coordinates has been applied, the inverse transform (ax.transData.inverted().transform) allows a backtransformation. Now there is another dimension of the problem: How to assure that the offset remains the same independent of the zoom and size of the figure? This last point can be addressed by recalculating the offset each time a zooming of resizing event has taken place. Here is how a rainbow curve would look like produced by this method. And here is the code to produce the image. import numpy as np import matplotlib.pyplot as plt dpi = 100 def offset(x,y, o): """ Offset coordinates given by array x,y by o """ X = np.c_[x,y].T m = np.array([[0,-1],[1,0]]) R = np.zeros_like(X) S = X[:,2:]-X[:,:-2] R[:,1:-1] = np.dot(m, S) R[:,0] = np.dot(m, X[:,1]-X[:,0]) R[:,-1] = np.dot(m, X[:,-1]-X[:,-2]) On = R/np.sqrt(R[0,:]**2+R[1,:]**2)*o Out = On+X return Out[0,:], Out[1,:] def offset_curve(ax, x,y, o): """ Offset array x,y in data coordinates by o in points """ trans = ax.transData.transform inv = ax.transData.inverted().transform X = np.c_[x,y] Xt = trans(X) xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72. ) Xto = np.c_[xto, yto] Xo = inv(Xto) return Xo[:,0], Xo[:,1] # some single points y = np.array([1,2,2,3,3,0]) x = np.arange(len(y)) #or try a sinus x = np.linspace(0,9) y=np.sin(x)*x/3. fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi) cols = ["#fff40b", "#00e103", "#ff9921", "#3a00ef", "#ff2121", "#af00e7"] lw = 2. lines = [] for i in range(len(cols)): l, = plt.plot(x,y, lw=lw, color=cols[i]) lines.append(l) def plot_rainbow(event=None): xr = range(6); yr = range(6); xr[0],yr[0] = offset_curve(ax, x,y, lw/2.) xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.) xr[2],yr[2] = offset_curve(ax, xr[0],yr[0], lw) xr[3],yr[3] = offset_curve(ax, xr[1],yr[1], -lw) xr[4],yr[4] = offset_curve(ax, xr[2],yr[2], lw) xr[5],yr[5] = offset_curve(ax, xr[3],yr[3], -lw) for i in range(6): lines[i].set_data(xr[i], yr[i]) plot_rainbow() fig.canvas.mpl_connect("resize_event", plot_rainbow) fig.canvas.mpl_connect("button_release_event", plot_rainbow) plt.savefig(__file__+".png", dpi=dpi) plt.show() Method2 To avoid overlapping lines, one has to use a more complicated solution. One could first offset every point normal to the two line segments it is part of (green points in the picture below). Then calculate the line through those offset points and find their intersection. A particular case would be when the slopes of two subsequent line segments equal. This has to be taken care of (eps in the code below). from __future__ import division import numpy as np import matplotlib.pyplot as plt dpi = 100 def intersect(p1, p2, q1, q2, eps=1.e-10): """ given two lines, first through points pn, second through qn, find the intersection """ x1 = p1[0]; y1 = p1[1]; x2 = p2[0]; y2 = p2[1] x3 = q1[0]; y3 = q1[1]; x4 = q2[0]; y4 = q2[1] nomX = ((x1*y2-y1*x2)*(x3-x4)- (x1-x2)*(x3*y4-y3*x4)) denom = float( (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) ) nomY = (x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4) if np.abs(denom) < eps: #print "intersection undefined", p1 return np.array( p1 ) else: return np.array( [ nomX/denom , nomY/denom ]) def offset(x,y, o, eps=1.e-10): """ Offset coordinates given by array x,y by o """ X = np.c_[x,y].T m = np.array([[0,-1],[1,0]]) S = X[:,1:]-X[:,:-1] R = np.dot(m, S) norm = np.sqrt(R[0,:]**2+R[1,:]**2) / o On = R/norm Outa = On+X[:,1:] Outb = On+X[:,:-1] G = np.zeros_like(X) for i in xrange(0, len(X[0,:])-2): p = intersect(Outa[:,i], Outb[:,i], Outa[:,i+1], Outb[:,i+1], eps=eps) G[:,i+1] = p G[:,0] = Outb[:,0] G[:,-1] = Outa[:,-1] return G[0,:], G[1,:] def offset_curve(ax, x,y, o, eps=1.e-10): """ Offset array x,y in data coordinates by o in points """ trans = ax.transData.transform inv = ax.transData.inverted().transform X = np.c_[x,y] Xt = trans(X) xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72., eps=eps ) Xto = np.c_[xto, yto] Xo = inv(Xto) return Xo[:,0], Xo[:,1] # some single points y = np.array([1,1,2,0,3,2,1.,4,3]) *1.e9 x = np.arange(len(y)) x[3]=x[4] #or try a sinus #x = np.linspace(0,9) #y=np.sin(x)*x/3. fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi) cols = ["r", "b"] lw = 11. lines = [] for i in range(len(cols)): l, = plt.plot(x,y, lw=lw, color=cols[i], solid_joinstyle="miter") lines.append(l) def plot_rainbow(event=None): xr = range(2); yr = range(2); xr[0],yr[0] = offset_curve(ax, x,y, lw/2.) xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.) for i in range(2): lines[i].set_data(xr[i], yr[i]) plot_rainbow() fig.canvas.mpl_connect("resize_event", plot_rainbow) fig.canvas.mpl_connect("button_release_event", plot_rainbow) plt.show() Note that this method should work well as long as the offset between the lines is smaller then the distance between subsequent points on the line. Otherwise method 1 may be better suited.
The best that I can think of is to take your data, generate a series of small offsets, and use fill_between to make bands of whatever color you like. I wrote a function to do this. I don't know what shape you're trying to plot, so this may or may not work for you. I tested it on a parabola and got decent results. You can also play around with the list of colors. def rainbow_plot(x, y, spacing=0.1): fig, ax = plt.subplots() colors = ['red', 'yellow', 'green', 'cyan','blue'] top = max(y) lines = [] for i in range(len(colors)+1): newline_data = y - top*spacing*i lines.append(newline_data) for i, c in enumerate(colors): ax.fill_between(x, lines[i], lines[i+1], facecolor=c) return fig, ax x = np.linspace(0,1,51) y = 1-(x-0.5)**2 rainbow_plot(x,y)
matplotlib: Have axis maintaining ratio
I am new to matplotlib, and I have a very simple (I'm guessing) question. I have some data that need to be represented in a rectangle of 50x70 "units" (they're feet, actually representing a room) but I don't seem to be able to get matplotlib drawing a rectangle with the same scale on both axis and keeping the 50x70 "dimensions" at the same time. I've tried the following: import json import matplotlib import os import sys import traceback import matplotlib.pyplot as plt DATA_FILE = os.path.join(os.path.expanduser("~"), "results.json") FLOOR_DIMENSIONS = (50, 70) if __name__ == "__main__": if len(sys.argv) > 1: DATA_FILE = os.path.abspath(sys.argv[0]) print "Gonna see what happens with file %s" % DATA_FILE try: with open(DATA_FILE, 'r') as f: result_dict = json.load(f) except (IOError, OSError, ValueError), e: print "Received %s %s when trying to parse json from %s\n"\ "Showing traceback: %s" % (type(e), e, DATA_FILE, traceback.format_exc()) result_dict = {} for d_mac in result_dict: data = result_dict[d_mac] if len(data) < 3: continue x_s = list(d['x'] for d in data) y_s = list(d['y'] for d in data) plt.scatter(x_s, y_s, marker='o', c=numpy.random.rand(5,1), s=15) plt.xlim([0, FLOOR_DIMENSIONS[0]]) plt.ylim([0, FLOOR_DIMENSIONS[1]]) #plt.axis('equal') plt.show() sys.exit(0) Doing that, I get: Which draws my data inside an square, changing the X-Y scale (X is 50 points, and Y is 70, therefor Y shows "shrunk") Another option I tried was uncommenting the line saying plt.axis('equal'), but that "cuts" the Y axis (doesn't start in 0 and finishes in 70, but starts in 15 and ends in 55, probably because there's no data with y < 15 and y > 55) But I don't want that either, I want the "canvas" starting in Y=0 and ending in Y=70, and if there's no data just show an empty space. What I need is to draw something like this: which I got by manually re-sizing the window where the plot was rendered :-D Thank you in advance!
Add plt.axis('scaled'). edit: axis('image') may be better for your needs. More axis settings can be found in the documentation. import matplotlib.pyplot as plt import numpy as np xs = np.arange(50) ys = (np.random.random(50)*70) + 15 plt.scatter(xs,ys) plt.axis('image') plt.axis([0, 50, 0, 70]) plt.show() gives: In the updated example I know the ys actually has a maximum of ~85, the offset was just to demonstrate proper axis enforcement.
Loop plot for mulitiple data files - Python
I would like to write a program that creates 100 masked plots from a spread of 100 text files. i.e. for fnum in range(1,100,1): The text files are numbered xydata1.txt, xydata2.txt ... until xydata100.txt. How is this best done in Python? Below is my plotting program, where (file number fnum) = 1,2,3...100. fn = 'xydata'+fnum+'.txt' y = loadtxt(fn,unpack=True,usecols=[0]) x = loadtxt(fn,unpack=True,usecols=[1]) n = ma.masked_where(gradient(y) < 0, y) p = ma.masked_where(gradient(y) > 0, y) pylab.plot(x,n,'r',x,p,'g') pylab.savefig('data'+fnum+'.png') pylab.show()
Assuming Python 2.7 from glob import glob from pylab import * for fname in glob("xydata*.txt"): x, y = loadtxt(fname, unpack=True, usecols=[1, 0]) mask_inf = gradient(y) < 0 mask_sup = gradient(y) >= 0 plot(x[mask_inf], y[mask_inf], 'r') plot(x[mask_sup], y[mask_sup], 'g') legend(("grad(y) < 0", "grad(y) >= 0")) title(fname) savefig(fname.replace("xydata", "data").replace(".txt", ".svg")) clf() You can also use masked arrays. But the only advantage of them is to avoid allocating new memory. If your plots are small enough, you don't need them. By the way there is no "best answer".