Dave
Dave

Reputation: 6554

plotting angularly-wrapped data in cartesian space with matplotlib

Perhaps I've made the title more complicated than the question, but here goes...!

I have some angular data, contiguous in the x-y plane, that straddle the 360 => 0 degree line - ie, 358,359,0,1,2....

If I were plotting these and setting:

 plt.xlim(0,360)

I would of course have three dots at the far left of the plot, and two at the far right. You can see this in the (more complicated, and actual) plot here (x-axis limits deliberately reversed):

the angularly-wrapped dataset

What I'd really like is to have all dots plotted around the same position in the plot window, perhaps towards the centre of the plot. Under this scheme, the x-axis decreases to the left of the 360-0 degree border, and increases to the right.

I don't want to make any translations / shifts to the data itself (it's a large dataset, etc), so I'd be looking to do this with some matplotlib-trickery.

I plan on plotting the datapoints with hexbin, if that makes any difference.

Thanks for looking, and thank you in advance for your help,

Dave

Upvotes: 4

Views: 2057

Answers (1)

Joe Kington
Joe Kington

Reputation: 284602

I honestly think just transforming your data will be much faster. x[x>180] -= 360 is quite fast. Unless your dataset is several GB in size, the time it takes to transform your data will only be a few milliseconds.

So, here's the easy way (transforming your data):

import matplotlib.pyplot as plt
import numpy as np

# Generate data to match yours...
y = 60 * np.random.random(300) - 20
x = 60 * (np.random.random(300) - 0.5)
x[x < 0] += 360

# Transform the data back to a -180 to 180 range...
x[x > 180] -= 360

# Plot the data
fig, ax = plt.subplots()
ax.plot(x, y, 'b.')

# Set the ticks so that negative ticks represent >180 numbers
ticks = ax.get_xticks()
ticks[ticks < 0] += 360
ax.set_xticklabels([int(tick) for tick in ticks])

plt.show()

enter image description here

However, if you want to avoid transforming your data you can do something like this... This is 100% guaranteed to be slower than just transforming your data, though. (Probably negligibly slower, but it won't be faster.)

import matplotlib.pyplot as plt
import numpy as np

# Generate data to match yours...
y = 60 * np.random.random(300) - 20
x = 60 * (np.random.random(300) - 0.5)
x[x < 0] += 360

fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True)
fig.subplots_adjust(wspace=0)

ax1.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax1.tick_params(right=False)
ax2.tick_params(left=False)
for label in ax2.get_yticklabels():
    label.set_visible(False)

ax1.plot(x[x > 180], y[x > 180], 'b.')
ax2.plot(x[x <= 180], y[x <= 180], 'b.')

ax2.set_xticks(ax2.get_xticks()[1:])

plt.show()

enter image description here

Upvotes: 5

Related Questions