Hi, I'm Andy!

Biosensing, Brain-computer interfaces, CS + HCI at Carnegie Mellon

Speed Check — Generating a list out of a range out of a len

Published November 25, 2020


As part of my PPG project, I had to create a live-updating plot. I'm most familiar with the matplotlib library, which has the same color scheme as MATLAB, so I'm using that. One problem is that they try to make a general-purpose plotting tool (matplotlib.pyplot) that also leaves you free to not worry about which window holds which figure and which renderer is drawing what, when. This is really bad because more general tools are more complex to use, and this is definitely the case here.

It's usually imported as import matplotlib.pyplot as plt. To create a 2-part plot involves creating Axes, which are done by plt.subplot(rows, cols). Then you write line = ax.plot(x,y), which doesn't seem bad until you try to use the line object you just created, and then you find out you have to call line, _ = ax.plot(x,y) since it gives multiple things back.

I won't do a full rant since that's not the point, but basically it's annoying, and if you call ax.plot() again, it creates a new line that overlaps with the old line. It's a pain. I needed two live-updating plots, so you can save the line object as a variable and call line.set_data(x,y) to change the data without adding a new line onto the graph.

Now, my data was time-series, and I just wanted the index of the array as the x axis. So for data like [0.5, 1.3, 2.4], I wanted an x list that was [1, 2, 3]. This is easy enough, it's just list(range(len(data))). And no, I tried using just range(len(data)). It has to be complete. I also thought of using enumerate, and taking only the first element in a list-comprehension. So I decided to compare them.

This was my first time using the timeit library, and as far as I could tell, it was kinda spotty in terms of consistency. Here's the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import timeit

uniqueTimes = 10
b = [0]*10000

def lenList(lst):
    return [x1 for x1,x2 in enumerate(lst)]
# 0.7 ns/elem

def lenList2(lst):
    return list(range(len(lst)))
# 0.22 ns/elem

def mapList3(elem, count = 0):
    count += 1
    return count-1
# 3.7 ns/elem

functs = [lambda : lenList(b),
            lambda : lenList2(b),
            lambda : list(map(mapList3, b))]

timesEach = 1000
for i in range(len(functs)):
    times = []
    for j in range(uniqueTimes):
        times.append(timeit.timeit(functs[i], number=timesEach))
    print(i, "ns/elem:", sum(times)/timesEach*1e6/len(b))

Turns out the naive list(range(len(x))) solution was the fastest! Until next time, cya!