Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Make YearLocator a subclass of RRuleLocator#19348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
timhoffm merged 7 commits intomatplotlib:masterfromtheOehrly:rrule-year-locator
May 26, 2021

Conversation

theOehrly
Copy link
Contributor

PR Summary

This makes the YearLocator a subclass of RRuleLocator instead of DateLocator.

Advantanges:

  • less custom code, tick positions are now determined through rrule, pytz is now handled through the rrulewrapper
  • easier implementation of locators for potential timedelta support (Add support for handling timedelta #19236)

PR Checklist

  • Has pytest style unit tests (andpytest passes).
  • IsFlake 8 compliant (runflake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs shouldbuild without error).
  • Conforms to Matplotlib style conventions (installflake8-docstrings and runflake8 --docstring-convention=all).
  • New features have an entry indoc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented indoc/api/next_api_changes/ (follow instructions in README.rst there).

@@ -1482,52 +1510,9 @@ def __init__(self, base=1, month=1, day=1, tz=None):
Mark years that are multiple of base on a given month and day
(default jan 1).
"""
super().__init__(tz)
self.base = ticker._Edge_integer(base, 0)
self.replaced = {'month': month,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

overall this seems fine. I suspect you need a minor API note that saysself.replaced has been dropped. This should probably never have been public in the first place, as I can't see why a user would do anything with it, but...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Is it worth putting in the a property what warns on get/set? On one hand, most likely no one is using this, on the other hand it is not much work to make sure we do not needlessly break anyone who is trying to access this.

Copy link
Member

@jklymakjklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I believe you that YEARLY needs the special logic, but without experimenting with it, I don't quite understand why... Overall, this seems like a good idea, but lets just make sure its clear and has tests..

@@ -0,0 +1,5 @@
``dates.YearLocator`` attribute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
``dates.YearLocator`` attribute
Removed``dates.YearLocator.replaced`` attribute

``dates.YearLocator`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The attribute ``YearLocator.replaced`` has been removed. For tick locations which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
Theattribute ``YearLocator.replaced`` has been removed. For tick locationswhich
`.YearLocator` is now a subclass of `RRuleLocator`, and theattribute ``YearLocator.replaced`` has been removed. For tick locationsthat

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this is fine. I'm guessing no one used this undocumented attribute.

@@ -1161,10 +1161,16 @@ def nonsingular(self, vmin, vmax):
class RRuleLocator(DateLocator):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Sorry, I should have noticed this the first time. You have also changed the signature of RRuleLocator. I think you need to write a new docstring for it, since it cannot simply inherit fromDateLocator any longer.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm actually not completely happy with that signature change yet. Calling the argument 'interval_multiples' is a bit confusing if it really only applies to yearly ticks.

Taking care of the special case directly in the RRuleLocator is best for overall clarity, imo. That's why I think this change is an improvement.

But I also want to achieve easy usage of the RRuleLocator for timedeltas at the same time. It already works for all frequencies except for daily. Daily with timedeltas is a very similar special case to yearly with dates. I'd like to subclass the RRuleLocator and only override init and the method that determines start, stop as multiples of the interval.

It might be better to have the signature be different between RRuleLocator and a subclass to make everything more clear?
For example, call the argument 'yearly_interval_multiples' maybe?

It would be best to have only one RRuleLocator class for dates and timedeltas. Also because of the locator classes for specific frequencies. As it looks, HourLocator can be used for dates and timedeltas. But DayLocator cannot be used for both because that is a special case with timedeltas. Ideally, all locators can be used for both. But I see no way currently to make that happen.

This is, of course, out of scope for this PR. But I do want to consider it already to facilitate the other PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

If it really can't be done generally, then maybe a subClass make sense. In that case you could make it the same as the previous YearlyLocator, but hopefully with less repeated code because you now subclass RRuleLocator. That seems the most straightforward, and it stops RRuleLocator from having a bunch of extra logic?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

That might actually be the best option. It still requires overwriting the.tick_values method in the yearly locator. There will be some very similar but slightly different code then. But less complex than the previous YearLocator. So an improvement after all.
And it keeps the extra logic out of the RRuleLocator, that's true. And it's not changing the signature of RRuleLocator.

But it also means that this has to stay inAutoDateLocator.get_locator():

  if (freq == YEARLY) and self.interval_multiples:      locator = YearLocator(interval, tz=self.tz)  elif use_rrule_locator[i]:      ...      locator = RRuleLocator(rrule, self.tz)

I don't particularly like this because it is not very flexible, considering an AutoTimedeltaLocator requires a different special case here. But maybe that's not a problem to solve here.

@@ -1175,22 +1181,28 @@ def __call__(self):
return self.tick_values(dmin, dmax)

def tick_values(self, vmin, vmax):
delta = relativedelta(vmax, vmin)
if self.mult_base:
# start and stop need to be multiples of the interval
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm not sure I follow this. Why do start and stop need to be multiples of the intervals? This either needs more comment, or cleaning up?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

More comments probably? So, usually creating ticks on multiples of the interval is done by adjusting the "byranges". So for example with frequency =SECONDLY and interval = 15, the AutoDateLocator makesbyseconds = [0, 15, 30, 45].

But there is no "byrange" for years. Rrule can create a reoccurrence every 10 years (for example). But you cannot specify that you want that to be on every full decade only. It will be every 10 years starting with the start date. So to get multiples of the interval, start needs to be a multiple of the interval.

This is why AutoDateLocator currently returns an RRuleLocator for YEARLY when.interval_multiples = False, but a YearLocator for when it is true.

Stop can technically be a value greater than the last desired tick.

The comment is not very clear, I agree. It should probably be something like "start needs to be a multiple of the interval for ticks on interval multiples when freq == YEARLY"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

But there is no "byrange" for years. Rrule can create a reoccurrence every 10 years (for example). But you cannot specify that you want that to be on every full decade only. It will be every 10 years starting with the start date. So to get multiples of the interval, start needs to be a multiple of the interval.

Probably something like this in the comments? That makes sense...

ticks.append(dt)
rule = rrulewrapper(YEARLY, interval=base, bymonth=month,
bymonthday=day, **self.hms0d)
super().__init__(rule, tz, interval_multiples=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This needs to get hit by the tests somehow! Maybe not your fault - i.e. perhaps YEARLY wasn't in the tests really well before, but the new functionality should get a test.

theOehrly reacted with thumbs up emoji
@theOehrly
Copy link
ContributorAuthor

Ok, some changes, sort of like you suggested.
I think this adds some flexibility while keeping everything rather simple.

@theOehrly
Copy link
ContributorAuthor

I played around with this some more, also in reference to#19236. This is basically the same special handling that's necessary for the DayLocator for timedeltas, just some slightly different math.
What I'm not sure about is, if it is really preferable to split .tick_values as I have done now. Yes, this prevents some duplicate code. On the other hand, it adds additional complexity.

@jklymak
Copy link
Member

@theOehrly this seemed reasonable. Sorry it languished. where is it at? And please do ping for reviews if needed...

@jklymakjklymak marked this pull request as draftMay 10, 2021 16:31
@theOehrly
Copy link
ContributorAuthor

It works and the created ticks are exactly the same as with the current YearLocator in all test cases that I tried.

Maybe it needs another small test added to get full coverage.

@theOehrly
Copy link
ContributorAuthor

@jklymak From my point of view it works. So I guess it can be reviewed in cases you're generally interested in having this merged.

(I guess I need to use pings more?)

@jklymakjklymak marked this pull request as ready for reviewMay 17, 2021 18:46
start, stop = self._create_rrule(vmin, vmax)
dates = self.rule.between(start, stop, True)
if len(dates) == 0:
return date2num([vmin, vmax])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes can you add a test for this. Otherwise I think this is good?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@jklymak
Copy link
Member

Closer!/home/circleci/project/doc/api/next_api_changes/removals/19348-OE.rst:4: WARNING: py:obj reference target not found: RRuleLocator

@theOehrly
Copy link
ContributorAuthor

@jklymak I think now everything is right.

I couldn't open the log of those test runs, so I didn't kown why they were failing. Should everybody be able to see that?

@jklymak
Copy link
Member

Yes I think everyone can see them but you have to download the log because it's usually too long.

Copy link
Member

@jklymakjklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Looks good to me. I think this is a clear improvement of the YearLocator.

@jklymak
Copy link
Member

I'll mark this as merge with single review and merge in a week if there are no further comments. Thanks!

@tacaswell
Copy link
Member

I do not think anything that requires an API change note should be merged on a single review.

@jklymak
Copy link
Member

I guess I think that depends on the intrusiveness of the API change. In this case,YearLocator.replaced was untested and undocumented as external API, and I don't think it was ever meant to be external API.

@timhoffmtimhoffm merged commit3590c26 intomatplotlib:masterMay 26, 2021
@timhoffm
Copy link
Member

@theOehrly Thanks and congratulations on your first contribution to Matplotlib! We hope to hear from you again.

@jklymak
Copy link
Member

I think it was fine to merge this as-is, but@tacaswelldid suggest a change above. I've opened an issue#20313 to track that suggestion.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tacaswelltacaswelltacaswell left review comments

@jklymakjklymakjklymak approved these changes

@timhoffmtimhoffmtimhoffm approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
v3.5.0
Development

Successfully merging this pull request may close these issues.

5 participants
@theOehrly@jklymak@tacaswell@timhoffm@QuLogic

[8]ページ先頭

©2009-2025 Movatter.jp