-
Notifications
You must be signed in to change notification settings - Fork 12
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature]: custom seasons that span calendar years #416
Comments
👍 to this request. I have an analysis right now where I'm interested in a (southern hemisphere) growing season NDJFM. Ideally I'd like to run the following: ds.temporal.group_average(
'pr',
freq='season',
season_config={
'dec_mode': 'DJF',
'drop_incomplete_djf': True,
'custom_seasons': ['Nov', 'Dec', 'Jan', 'Feb', 'Mar']
}
) Assuming my input dataset |
Thank you for opening this issue @arfriedman! Also thank you @DamienIrving for your input! This feature enhancement definitely sounds useful. Here are the set of improvements based on the feedback:
|
@tomvothecoder as discussed in the meeting, something like below converter could help make the custom season function to be easier to use. def generate_calendar_months(custom_season, output_type: str = "month_abbreviations"):
"""
Generates a list of calendar months corresponding to the given custom season.
Args:
custom_season (str): A string representing a custom season (e.g., "MJJ").
output_type (str, optional): default is "month_abbreviations" which returns month abbreviations. If set to "month_numbers", it will return months in numbers.
Returns:
list: A list of strings of calendar months corresponding to the given custom season, or a list of numbers
Raises:
ValueError: If the length of the custom season is longer than 12 or if the custom season is not found in the months.
ValueError: If `output_type` is not one of "month_abbreviations" or "month_numbers"
Example:
>>> generate_calendar_months("MJJ")
['May', 'Jun', 'Jul']
"""
# Define the mapping of month abbreviations to full month names
months_mapping = [
("J", "Jan", 1), ("F", "Feb", 2), ("M", "Mar", 3), ("A", "Apr", 4),
("M", "May", 5), ("J", "Jun", 6), ("J", "Jul", 7), ("A", "Aug", 8),
("S", "Sep", 9), ("O", "Oct", 10), ("N", "Nov", 11), ("D", "Dec", 12)
] * 2 # Repeat the mapping to cover cases where the custom season wraps around to the beginning of the year
# Generate a string representation of all months by concatenating their abbreviations
months = ''.join([m[0] for m in months_mapping])
# Check if the length of the custom season exceeds 12
if len(custom_season) > 12:
raise ValueError("Custom season length cannot be longer than 12")
if output_type == "month_abbreviations":
k = 1
elif output_type == "month_numbers":
k = 2
else:
raise ValueError(f"{output_type} should be either of 'month_abbreviations' or 'numbers'")
# Iterate through the months to find the starting index of the custom season
for i in range(len(months) - len(custom_season) + 1):
if months[i:i+len(custom_season)] == custom_season:
# Once the custom season is found, return the corresponding list of months
return [months_mapping[(i + j) % 12][k] for j in range(len(custom_season))]
# If the custom season is not found, raise a ValueError
raise ValueError("Custom season '{}' not found in months '{}'".format(custom_season, months)) Test, return as month abbreviations: custom_season = "MJJAS"
result = generate_calendar_months(custom_season)
print(result)
custom_season = "NDJFM"
result = generate_calendar_months(custom_season)
print(result)
custom_season = "JJASONDJFMAM"
result = generate_calendar_months(custom_season)
print(result)
Test 2, return as month numbers: custom_season = "MJJAS"
result = generate_calendar_months(custom_season, output_type="month_numbers")
print(result)
custom_season = "NDJFM"
result = generate_calendar_months(custom_season, output_type="month_numbers")
print(result)
custom_season = "JJASONDJFMAM"
result = generate_calendar_months(custom_season, output_type="month_numbers")
print(result)
Test 3, error cases: custom_season = "JAM"
result = generate_calendar_months(custom_season)
custom_season = "JFMAMJJASONDJ"
result = generate_calendar_months(custom_season)
|
@tomvothecoder please feel free to incorporate the above code if you find it is useful. No worries otherwise. |
@lee1043 Thanks Jiwoo! I'll consider your function for improving the |
Here's an upstream version: pydata/xarray#9524 . I could use some help testing it out. |
Is your feature request related to a problem?
Following #393, it seems that it would be useful to expand custom seasons functionality across calendar years. Examples include: taking the water year from October to September, or taking a boreal winter average from December to March.
Describe the solution you'd like
I envision the main change would be so that the order of months listed in custom seasons does matter, and could span across calendar years.
For example, this configuation would create Apr-Nov and Dec-March averages, the latter which extends into the following year:
Associated with this change I think it would also make sense to generalize the season_config parameters beyond the existing options for DJF, e.g. the flag
“drop_incomplete_djf”
could become something like"drop_incomplete_season."
In addition to seasons that cross the calendar year, this could apply to datasets that end in the middle of a season (for example, the last Apr-June season for a dataset that ends in May).Describe alternatives you've considered
No response
Additional context
As a possible aside, I was also wondering if it is necessary to keep the requirement to include all 12 months in the
custom_seasons
list. I imagine that often users are only interested in one season.The text was updated successfully, but these errors were encountered: