Plotting a PDF of an angular distribution in Python

98 Views Asked by At

I am trying to plot the PDF of some data I have on the angular orientation of a particles in python, using sns. The data cover the -180,180 degree range and I have problems with the fitting of the distribution especially around the edges

For this image in particular, I used histplot to compare the fit to the histogram of the data (I also tried kdeplot and the result is the same) and you can see the PDF doesn't cover the edges and the sharp transition in the middle

This is the snippet of code I use for the plotting

for shear_value, color in zip(unique_shear_values, shear_palette_colors):
    subset = orientations[orientations['Shear'] == shear_value]
    sns.histplot(data=subset['Angle'], stat="density", kde=True, label=f'Shear: {shear_value}', color=color, bins = 128)

Is there any way to obtain a PDF that better fit the behavior of the data, especially at the edges?

1

There are 1 best solutions below

0
JohanC On BEST ANSWER

A trick could be to copy all data with the angles below 0 adding 360º. And similarly copying all data with angles above 0 subtracting 360º. Then create the histogram with the double of bins. Also limit the x-axis to the range -180 +180.

A drawback is that the y-axis will show everything with half the height. Creating a dummy twin axis allows to show the correct y-scaling.

The following code illustrates the idea:

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# create some reproducible test data
np.random.seed(20231011)
y, x = np.random.randn(2, 600, 3).cumsum(axis=1).reshape(2, -1)
angles = np.degrees(np.arctan2(y, x))

# double the range by repeating the angles to the left and to the right
angles = np.concatenate([angles, angles[angles < 0] + 360, angles[angles >= 0] - 360])

fig, ax = plt.subplots()

ax_twinx = ax.twinx()
sns.histplot(angles, kde=True, stat='density',
             binrange=(-360, 360), bins=360, kde_kws={'bw_adjust': 0.5}, ax=ax_twinx)
ax.set_xlim(-180, 180)
ax.set_ylim(0, ax_twinx.get_ylim()[1] * 2) # set the limits to twice the limits of the dummy axis
ax_twinx.set_yticks([]) # remove the ticks of the dummy axis
ax.set_ylabel(ax_twinx.get_ylabel()) # copy the y-label
ax_twinx.set_ylabel(None) # remove the label of the dummy y-axis
sns.despine()
ax.set_title('repeating angles left and right')

plt.tight_layout()
plt.show()

extending the range for a kdeplot of angles