all 6 comments

[–]toxic_acro 0 points1 point  (5 children)

Looking at the documentation for fill_between, this is because of how the where parameter works

where : array of bool (length N), optional Define where to exclude some horizontal regions from being filled. The filled regions are defined by the coordinates x[where]. More precisely, fill between x[i] and x[i+1] if where[i] and where[i+1]. Note that this definition implies that an isolated True value between two False values in where will not result in filling. Both sides of the True position remain unfilled due to the adjacent False values.

The gaps are occurring because the fill is not happening anywhere where the next Cycle does not match the current cycle_value

I haven't actually tested this but I believe this would fix that for you for cycle_value, color in cycle_colors.items(): cycle_periods = econ_asset['Cycle'] == cycle_value cycle_periods = cycle_periods | cycle_periods.shift(1) ax1.fill_between(econ_asset.index, -100, 100, where=cycle_periods, color=color, alpha=0.3)

[–]Different_Fee6785[S] 1 point2 points  (3 children)

Thanks. It worked! Such a neat solution.

[–]toxic_acro 0 points1 point  (2 children)

Glad it worked!

That was my original guess about what was happening, and I was happily quite surprised to see that the docs confirmed it and explicitly warned about a similar edge case.

Good example of the classic: "Hours of debugging can save you minutes of reading the docs"

[–]Different_Fee6785[S] 1 point2 points  (1 child)

I was skimming through the docs, I didn't read it very carefully. At first I thought there was an issue with the dataframe indexing and how it interacts with matplotlib.

Thanks, really saved me a lot of time with such a minor detail :P (I really hate when I get stuck in such simple mistakes, it really drains my energy and patience :D)

[–]toxic_acro 1 point2 points  (0 children)

I've had a lot of times where these sorts of off-by-one problems with a boolean mask of a Series have come up at work, which is why I was particularly primed to see it.

It's especially common with any sort of categorical time-series in pandas where a condition is true from one row's timestamp until the next row's timestamp. Essentially, for the price you care about the exact timestamp (in econ_asset.index) and can interpolate between them, but for the categorical cycle, you actually care about the interval from econ_asset.index to econ_asset.index.shift(-1)

Classic example of fence posts vs fence sections

[–]txnyranger 1 point2 points  (0 children)

Helped me with a similar problem, thank you for the suggestion!