You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm somewhat confused about how hypnograms are represented with yasa.plot_hypnogram, and even somewhat suspicious that the plotted hypnogram is shifted (left) by 1 epoch. I suppose there are multiple ways to view a graph like this, especially how much interpretation you might put on vertical lines. I think the hypnogram includes stages that represent a chunk of time (e.g., epoch 1 with standard 1/30 sf represents the window between 0-30 seconds), whereas the current plotting with plt.step doesn't handle that well.
Examples below, but first I'll jump ahead to my conclusion: Maybe a combined use of ax.hlines (for the lines) and ax.stairs (for the fill) would make for a better and more accurate visualization?
This example shows plots of 2 different hypnograms that should be 5 minutes long. They only differ by whether it takes 30 or 60 seconds to fall asleep. Note the difference in the first Wake epoch(s) between the two graphs. In the first graph, it should be 60 seconds long but it's 30. In the second graph, it should be 30 seconds long but it's... 0?
This time using YASA to make sure my code wasn't different from the underlying function.
#### Silly 2-epoch example to highlight the problem.yasa.plot_hypnogram([0, 1], sf)
plt.suptitle("1-min hypnogram: W - N1")
plt.tight_layout()
Alternatives
I like the use of plt.stairs() because of the explicit passing of bin edges. Also the fill option is nice, though you can see that in a case where a hypnogram ends on non-wake, it will automatically jump up to 0. This can be changed with parameters! Just pointing it out as something to be careful about with defaults. I actually like plt.hlines() because it avoids the whole vertical line thing.
# Make a super-simple super-short hypnogram.# (5-minutes, decreasing through 2 30-s epochs of each stage.)hypno_str= ["W", "W", "N1", "N1", "N2", "N2", "N3", "N3", "R", "R"]
hypno_int=yasa.hypno_str_to_int(hypno_str)
# Some transformations usually done within yasa.plot_hypnogramhypno_yvals=pd.Series(hypno_int
).map({-2: -2, -1: -1, 0: 0, 1: 2, 2: 3, 3: 4, 4: 1}
).mul(-1).valuesn_epochs=hypno_yvals.size# x-values for plt.step, same size as hypnot_hyp=np.arange(n_epochs) / (sf*60)
# x-values for plt.stairs and plt.hlines, 1 longer than hypnobins=np.arange(n_epochs+1) / (sf*60)
# Draw 3 different plots of the same datafig, axes=plt.subplots(nrows=3, figsize=(4, 6), sharex=True, sharey=True)
axes[0].step(t_hyp, hypno_yvals, color="black", lw=2)
axes[0].set_title("ax.step()")
axes[1].stairs(hypno_yvals, bins, color="gainsboro", fill=True)
axes[1].stairs(hypno_yvals, bins, lw=2, color="black")
axes[1].set_title("ax.stairs(fill=True)")
axes[2].hlines(hypno_yvals, xmin=bins[:-1], xmax=bins[1:], lw=2, color="black")
axes[2].set_title("ax.hlines()")
# Titles/labels/limitsaxes[2].set_xlim(-0.5, 5.5)
axes[2].set_ylim(-4.5, 0.5)
axes[2].set_yticks([0, -1, -2, -3, -4])
axes[2].set_yticklabels(["W", "R", "N1", "N2", "N3"])
fig.suptitle("5-min hypnogram:\n"+" - ".join(hypno_str))
fig.supxlabel("Time [mins]")
plt.tight_layout()
YASA's function
Again just showing that my code is providing the same output as YASA. This should match the first panel of previous image, where there should be 60 seconds of Wake to start, but there's 30.
Thanks for the super detailed PR, loved it! Let's go with plt.stairs, it is indeed much better than the current plt.step. I think plt.hlines gets messy with longer hypnograms. I like the fill parameter too, definitely would like to add this as a customization parameter.
I'm somewhat confused about how hypnograms are represented with
yasa.plot_hypnogram
, and even somewhat suspicious that the plotted hypnogram is shifted (left) by 1 epoch. I suppose there are multiple ways to view a graph like this, especially how much interpretation you might put on vertical lines. I think the hypnogram includes stages that represent a chunk of time (e.g., epoch 1 with standard 1/30 sf represents the window between 0-30 seconds), whereas the current plotting withplt.step
doesn't handle that well.Examples below, but first I'll jump ahead to my conclusion: Maybe a combined use of
ax.hlines
(for the lines) andax.stairs
(for the fill) would make for a better and more accurate visualization?Examples
Using some super-short simple hypnograms...
5-min hypnograms aren't 5 minutes
This example shows plots of 2 different hypnograms that should be 5 minutes long. They only differ by whether it takes 30 or 60 seconds to fall asleep. Note the difference in the first Wake epoch(s) between the two graphs. In the first graph, it should be 60 seconds long but it's 30. In the second graph, it should be 30 seconds long but it's... 0?
Zoom in on 1-min: flat line despite 2 stages
This time using YASA to make sure my code wasn't different from the underlying function.
Alternatives
I like the use of
plt.stairs()
because of the explicit passing of bin edges. Also thefill
option is nice, though you can see that in a case where a hypnogram ends on non-wake, it will automatically jump up to 0. This can be changed with parameters! Just pointing it out as something to be careful about with defaults. I actually likeplt.hlines()
because it avoids the whole vertical line thing.YASA's function
Again just showing that my code is providing the same output as YASA. This should match the first panel of previous image, where there should be 60 seconds of Wake to start, but there's 30.
The text was updated successfully, but these errors were encountered: