Meridian version: 1.7.0 (also happens on older versions)
Python: 3.12
What happens
Calling save_meridian on a model with calendar-monthly time coordinates (1st of each month) raises:
ValueError: Interval length between selected times must be consistent.
The odd part is that the same model passes Meridian's own InputData validation and trains and analyses fine. Only saving to .binpb fails. So the input layer treats monthly data as valid but the serializer does not.
Reproduce
from meridian.schema.serde import meridian_serde
# mmm has monthly time coords: 2024-01-01, 2024-02-01, 2024-03-01, ...
# day gaps are 31, 29, 31, 30, ...
meridian_serde.save_meridian(mmm, "model.binpb")
Traceback (trimmed):
meridian/schema/serde/marketing_data.py in __call__(self)
--> times_to_date_intervals = time_record.convert_times_to_date_intervals(
self._input_data.media_time.data)
meridian/schema/utils/time_record.py in convert_times_to_date_intervals(times)
--> raise ValueError("Interval length between selected times must be consistent.")
What I found
The error comes from convert_times_to_date_intervals in meridian/schema/utils/time_record.py. It takes the gap between the first two dates as the expected interval, then requires every following gap to match it exactly:
interval_length = _compute_interval_length(datetimes[0], datetimes[1]) for i, start_date in enumerate(datetimes):
...
if current_interval_length != interval_length: raise ValueError("Interval length between selected times must be consistent.")
Calendar months are 28/29/30/31 days, so the second gap already differs and it raises.
Meanwhile _is_regular_time_index in meridian/data/time_coordinates.py (used by InputData._validate_times) explicitly allows monthly (28 to 31), quarterly (90 to 92) and yearly (365 to 366) spacing. the two checks disagree.
As far as I can tell the format itself does not need uniform spacing. The serializer writes an explicit DateInterval (start, end) onto every data point from the actual dates, and deserialize reads each time back from its stored start date. The only place a single interval is actually neede of the last period, which has no next date to look at.
Possible fix
Drop the strict check and derive the last period's end from the final gap instead of the first one:
final_interval_length = _compute_interval_length(datetimes[-2], datetimes[-1])
for i, start_date in enumerate(datetimes):
end_date = (start_date + timedelta(days=final_interval_length)
if i == len(datetimes) - 1 else datetimes[i + 1])
...
That would accept any increasing time axis, which lines up with what InputData alreadyhe mean interval (interval_days) which already tolerates monthly, so it should be fine.
Question
Is monthly data meant to be supported here, or is the uniform interval requirement intentional for .binpb? If it is supposed to work, is this something that could be fixed? Happy to help or put up a PR if that would be useful.
Meridian version: 1.7.0 (also happens on older versions)
Python: 3.12
What happens
Calling
save_meridianon a model with calendar-monthly time coordinates (1st of each month) raises:The odd part is that the same model passes Meridian's own
InputDatavalidation and trains and analyses fine. Only saving to.binpbfails. So the input layer treats monthly data as valid but the serializer does not.Reproduce
Traceback (trimmed):
What I found
The error comes from
convert_times_to_date_intervalsinmeridian/schema/utils/time_record.py. It takes the gap between the first two dates as the expected interval, then requires every following gap to match it exactly:Calendar months are 28/29/30/31 days, so the second gap already differs and it raises.
Meanwhile
_is_regular_time_indexinmeridian/data/time_coordinates.py(used byInputData._validate_times) explicitly allows monthly (28 to 31), quarterly (90 to 92) and yearly (365 to 366) spacing. the two checks disagree.As far as I can tell the format itself does not need uniform spacing. The serializer writes an explicit
DateInterval(start, end) onto every data point from the actual dates, and deserialize reads each time back from its stored start date. The only place a single interval is actually neede of the last period, which has no next date to look at.Possible fix
Drop the strict check and derive the last period's end from the final gap instead of the first one:
That would accept any increasing time axis, which lines up with what
InputDataalreadyhe mean interval (interval_days) which already tolerates monthly, so it should be fine.Question
Is monthly data meant to be supported here, or is the uniform interval requirement intentional for
.binpb? If it is supposed to work, is this something that could be fixed? Happy to help or put up a PR if that would be useful.