1

I do have the following data:

  • x_values (which are monotonic ascending from about -300 to 100)
  • y1_values (which are random from 0 to 150)
  • y2_values (which are random from -1 to 1)
  • colorInfo (which can be 0, 1, 2 (for blue, red, green))

So: Now I want to plot y1 and y2 over x, where the plot is colored in the color given by colorInfo

My data (which I get from a csv file) could look like this:

   x   |   y1   |   y2   |   color
 -300  |  50    |   0.5  |     0
 -298  |  51    |   0.4  |     0
 -295  |  51    |   0.2  |     1
 -292  |  44    |   0.1  |     1

So I want the plot of y1 and y2 to be colored like:

  • from -300 to -298: blue

  • from -298 to -295: blue

  • from -295 to -292: red

Now I have the following code (which is basically a modified version of: https://matplotlib.org/1.5.0/examples/pylab_examples/multicolored_line.html):

import csv, os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
import tkinter as tk
from tkinter import filedialog

def plotter(filepath):
    data = read_csv(os.path.abspath(filepath))
    x_values = [None if data["x"] is '' else float(data["x"]) for data["x"] in data["x"]]
    y1_values = [None if data["y1"] is '' else float(data["y1"]) for data["y1"] in data["y1"]]
    y2_values = [None if data["y2"] is '' else float(data["y2"]) for data["y2"] in data["y2"]]
    colorInfo = [None if data["color"] is '' else float(data["color"]) for data["color"] in data["color"]]

    cmap = ListedColormap(['b', 'r', 'g'])
    norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)


    points = np.array([x_values, y1_values]).T.reshape(-1, 1, 2)
    segments = np.concatenate([points[:-1], points[1:]], axis=1)

    lc = LineCollection(segments, cmap=cmap, norm=plt.Normalize(0, 10))
    lc.set_array(colorInfo)
    lc.set_linewidth(3)

    fig1 = plt.figure()
    plt.gca().add_collection(lc)
    plt.show()

def read_csv(filename):
    data = {}
    with open(filename) as csvfile:
        csvreader = csv.DictReader(csvfile, delimiter=',')
        for row in csvreader:
            for key in row.keys():
                if key not in data:
                    data[key] = []
                data[key].append(row[key])
    return data


if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()

    file_path = filedialog.askopenfilename()
    print(file_path)
    plotter(file_path)

the minimal csv file is:

x,y1,y2,color
-300,40,1,0
-298,42,1,0
-291.2,44,1,0
-261,48,0.6,1
-245,51,0.5,1
-236,54,0.5,1
-221,48,0.3,1
-210,40,0.1,1
-150,38,-0.2,1
-130,37,-1,1
-110,35,-1,1
-50,30,0.5,2
-10,25,0.5,2
0,20,0.6,2
5,21,1,2
50,30,0.6,0
70,40,0.2,0
80,50,1,0
100,60,1,0

This only shows a white screen. I guess I need the lc.set_array. But I do have the problem that I can only set this to a function and not to my defined points.

Any suggestions?

7
  • set_array does not accept functions. It only accepts arrays or lists. Since you have your color information in a list, this should be directly applicable. If you have a problem with that you need to provide a runnable example (see minimal reproducible example) such that one can test at which point the problem occurs. Commented Apr 4, 2018 at 13:52
  • @ImportanceOfBeingErnest Thanks for the answer. If I use set_arrays I only get a black screen. I edited my code to be runnable and provided a small csv file to test with. Commented Apr 4, 2018 at 14:32
  • You have a problem with reading in your data, not with a matplotlib plot. Print your data to see how it is completely off. If you have a problem with reading your data, ask a question about that instead. Mind that there are much better ways to get csv data read into python than using open() on a file. Commented Apr 4, 2018 at 14:43
  • I copied the whole data reading from a working plot. Commented Apr 4, 2018 at 14:45
  • Not sure that is relevant. What matters is that you need to get arrays of your data somehow. Once you can do that, you may ask about plotting them. At the moment there is only an empty list, which cannot be plotted. Commented Apr 4, 2018 at 14:54

1 Answer 1

1

The reason why you are seeing a blank plot is because adding the LineCollection directly to an axis does not scale it, and the default scale from 0 to 1 does not include your data. So one immediate fix is just to call autoscale on the axis at the end of your plotting code (See Note 2 in the code). Furthermore, you don't need to pass the norm argument, as your color information is already categorical as required (see Note 1 in code). The code you are using to read the csv appears to work in my tests, but is quite strange and destroys your lists, so checking data["x"] after calling your function would give only one value instead of the original list.

Here is a version of the code which does the plotting from a file called test.csv which contains your test data. I've used pandas instead of the csv module to do the reading as it is more succinct.

import pandas
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap

df = pandas.read_csv('test.csv')

def colorline(x, y, c):
    cmap = ListedColormap(['b', 'r', 'g'])

    points = np.array([x, y]).T.reshape(-1, 1, 2)
    segments = np.concatenate([points[:-1], points[1:]], axis=1)

    # Note 1: You don't need the norm argument below with your data
    lc = LineCollection(segments, cmap=cmap)
    lc.set_array(c)
    lc.set_linewidth(3)

    return lc

fig, ax = plt.subplots(1, 1)
ax.add_collection(colorline(df.x, df.y1, df.color))
ax.add_collection(colorline(df.x, df.y2, df.color))

# Note 2: Adding the line collections directly doesn't cause autoscaling, so you have to call autoscale manually
ax.autoscale()

The graph which results from the code above looks like this:

enter image description here

Sign up to request clarification or add additional context in comments.

6 Comments

This fixed one issue. But my plot isn't colored correctly now
@ChrizZ I have added the output from my code to the answer. Perhaps you could explain how this is not what you had in mind?
It is exactly what I was looking for. I forgot to remove the norm, which colored my plot. thanks a lot
@ImportanceOfBeingErnest I've rolled back your edit but retained the autoscale bit. If you run the code in a debugger you can see that the CSV reading code and the conversion to floats actually works, but destroys the list it is iterating through, which may be why you thought the lists are read incorrectly.
The user attempted to plot an empty list, plot([],[]).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.