Jesse
Jesse

Reputation: 3

Python/Matplotlib - How to plot a vector sum on top of a circular histogram?

I am trying to plot both a circular histogram and a vector (overlapping) on the same polar plot, but cannot get the vector to show up.

Basically, my data set consists of the times at which unitary events occur during a repeating cycle. This data is in the array "all_phases", which is just a list of degree values for each of these events. I want to show (1) the overall distribution of events w/ a circular histogram (bins corresponding to degree ranges) and (2) a vector sum as a measure of the coherence of all of these values (treating each event as a unit vector).

I can plot either one of these things individually on the subplot titled "histo", but when I try to plot both, only the histogram shows up. I have tried playing with the z-indexes of both objects to no use. The code is:

import numpy as np 
import matplotlib.pyplot as plt
import matplotlib
import math

array = np.array

all_phases = [array([-38.24240218]), array([-120.51570738]), array([-23.70224663]), 
array([114.9540152]), array([ 2.94523445]), array([-2.16112692]), array([-18.72274284]),
array([13.2292216]), array([-95.5659992]), array([15.69046269]), array([ 51.12022047]), 
array([-89.10567276]), array([ 41.77283949]), array([-9.92584921]), array([-7.59680678]), 
array([166.71824996]), array([-178.94642752]), array([-23.75819463]), array([38.69481261]), 
array([-52.26651244]), array([-57.40976514]), array([33.68226762]), array([-122.1818295]), 
array([ 10.17007425]), array([-38.03726335]),array([44.9504975]), array([ 134.63972923]), 
array([ 63.02516932]),array([-106.54049292]), array([-25.6527599])]

number_bins = 60
bin_size = 360/number_bins
cluster_num = 1

counts, theta = np.histogram(all_phases, np.arange(-180, 180 + bin_size, bin_size), density=True)
theta = theta[:-1]+ bin_size/2.
theta = theta * np.pi / 180
a_deg = map(lambda x: np.ndarray.item(x), all_phases)
a_rad = map(lambda x: math.radians(x), a_deg)

a_cos = map(lambda x: math.cos(x), a_rad)
a_sin = map(lambda x: math.sin(x), a_rad)

uv_x = sum(a_cos)/len(a_cos)
uv_y = sum(a_sin)/len(a_sin)
uv_radius = np.sqrt((uv_x*uv_x) + (uv_y*uv_y))
uv_phase = np.angle(complex(uv_x, uv_y))
"""
plot histogram and vector sum
"""
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.16, 0.05, 0.56])

histo = fig.add_subplot(111, polar=True)
histo.yaxis.set_ticks(())
histo.arrow(0,0,0.11, 1, head_width=.01, zorder=2)
plt.suptitle("Phase distribution for Neuron #" + str(cluster_num), fontsize=15, y=.94)
plt.subplots_adjust(bottom=0.12, right=0.95, top=0.78, wspace=0.4)
width = (2*np.pi) / number_bins
bars = histo.bar(theta, counts, width = width, bottom=0.002)

for r, bar in zip(counts, bars):
    bar.set_facecolor(plt.cm.jet(r / max(counts)))
    bar.set_alpha(0.7)
    bar.set_zorder(1)
norm = matplotlib.colors.Normalize(vmin (counts.min())*len(all_phases)*bin_size, vmax=(counts.max())*len(all_phases)*bin_size)
cb1 = matplotlib.colorbar.ColorbarBase(ax1, cmap=plt.cm.jet,
                orientation='vertical', norm=norm, alpha=0.4,
                ticks=np.arange(0, (counts.max())*len(all_phases)*bin_size)+1, )
cb1.ax.tick_params(labelsize=9)
cb1.solids.set_rasterized(True)
cb1.set_label("# spikes")
cb1.ax.yaxis.set_label_position('left')

plt.show()
cluster_num = cluster_num + 1

vs_radius and vs_phase are the parameters for the vector sum arrow I want to put on the polar plot, which I end up calling w/ histo.arrow().

My suspicion is that it might have something to do with trying to put two things on a subplot object?

Any help or thoughts would be very much appreciated!!

Upvotes: 0

Views: 771

Answers (1)

Diziet Asahi
Diziet Asahi

Reputation: 40727

The problem is that the FancyArrow that is used by Axes.arrow() does not play well with polar plots. Instead, you could use the annotate() function to draw a simple arrow that works better in the case of polar plots.

for example:

# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)

ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)

# Use custom colors and opacity
for r, bar in zip(radii, bars):
    bar.set_facecolor(plt.cm.viridis(r / 10.))
    bar.set_alpha(0.5)


v_angle = 0.275*np.pi
v_length = 4

ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))

plt.show()

enter image description here

As a general rule, when you deal with polar plot, you have to work just as if you were working with a linear plot. That is to say, you shouldn't try to draw your arrow from (0,0) but rather from (uv_phase, 0)

fig, ax = plt.subplots()
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
    bar.set_facecolor(plt.cm.viridis(r / 10.))
    bar.set_alpha(0.5)

ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))

enter image description here

Upvotes: 0

Related Questions