bart cubrich
bart cubrich

Reputation: 1254

Faster way to provide rotation to scatter point plots in matplotlib?

Currently I use the following to plot a set of rotated lines (geologic strike indicators). However, this section of code takes a long time even with only a modest amount of strikes (5000). Each point has a unique rotation. Is there a way to give matplotlib a list with the rotations and perform the plotting faster than rotating one-by-one like this?

sample=#3d-array of points(x,y,theta) where theta is an amount I want to rotate the points by.

    for i in range(len(sample.T)):
        t = matplotlib.markers.MarkerStyle(marker='|')
        t._transform = t.get_transform().rotate_deg(sample[2,i])
        plt.scatter(sample[0,i],sample[1,i],marker=t,s=50,c='0',linewidth=1)

Upvotes: 1

Views: 1882

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339660

Here you create 5000 individual scatter plots. That is for sure inefficient. You may use a solution I proposed in this answer, namely to set the individual markers as paths to a PathCollection. This would work similar to a scatter, with an additional argument m for the markers.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.markers as mmarkers

def mscatter(x,y,ax=None, m=None, **kw):
    import matplotlib.markers as mmarkers
    if not ax: ax=plt.gca()
    sc = ax.scatter(x,y,**kw)
    if (m is not None) and (len(m)==len(x)):
        paths = []
        for marker in m:
            if isinstance(marker, mmarkers.MarkerStyle):
                marker_obj = marker
            else:
                marker_obj = mmarkers.MarkerStyle(marker)
            path = marker_obj.get_path().transformed(
                        marker_obj.get_transform())
            paths.append(path)
        sc.set_paths(paths)
    return sc


np.random.seed(42)
data = np.random.rand(5000,3)
data[:,2] *= 360

markers = []
fig, ax = plt.subplots()
for i in range(len(data)):
    t = mmarkers.MarkerStyle(marker='|')
    t._transform = t.get_transform().rotate_deg(data[i,2])
    markers.append(t)
mscatter(data[:,0], data[:,1], m=markers, s=50, c='0', linewidth=1)

plt.show()

If we time this we find that this takes ~250 ms to create the plot with 5000 points and 5000 different angles. The loop solution would in contrast take more than 12 seconds.

So far for the general question on how to rotate many markers. For the special case here, it seems you want to use simple line markers. This could easily be done using a quiver plot. One may then turn the arrow heads off to have the arrows look like lines.

fig, ax = plt.subplots()
ax.quiver(data[:,0], data[:,1], 1,1, angles=data[:,2]+90, scale=1/10, scale_units="dots",
          units="dots", color="k", pivot="mid",width=1, headwidth=1, headlength=0)

enter image description here

The result is pretty much the same, with the benefit of this plot only taking ~80 ms, which is again three times faster than the PathCollection.

Upvotes: 2

Related Questions