【发布时间】:2020-05-12 02:35:00
【问题描述】:
我继承了一个使用 DjangoRestFramework 的 AngularJS / Django 应用程序和一个 Postgres DB,它正在从 AngularJS 重新平台化为 React / Redux。 我们正在尝试做的一件事是使用 amCharts4 呈现时间序列数据。我们遇到的一个问题(以及许多其他问题)是在数据库中可能没有条目的时间范围内呈现数据。例如,我们的结果可能类似于:
[
{
"date": "2020-01-16T00:00:00.000Z",
"result": 3
},
{
"date": "2020-01-18T00:00:00.000Z",
"result": 2
}
]
并希望它们看起来像:
[
{
"date": "2020-01-16T00:00:00.000Z",
"result": 3
},
{
"date": "2020-01-17T00:00:00.000Z",
"result": 0
},
{
"date": "2020-01-18T00:00:00.000Z",
"result": 2
}
]
此外,我们还有每个时间事件具有多个数据点的数据:
[
{
"date": "2020-01-13T00:00:00Z",
"result": 1,
"name": "Yes"
},
{
"date": "2020-01-14T00:00:00Z",
"result": 1,
"name": "No"
},
{
"date": "2020-01-16T00:00:00Z",
"result": 1,
"name": "No"
}
]
并且希望在没有结果的任何日期为任何name 填充0 的数据:
[
{
"date": "2020-01-13T00:00:00Z",
"result": 1,
"name": "Yes"
},
{
"date": "2020-01-13T00:00:00Z",
"result": 0,
"name": "No"
},
{
"date": "2020-01-14T00:00:00Z",
"result": 0,
"name": "Yes"
},
{
"date": "2020-01-14T00:00:00Z",
"result": 1,
"name": "No"
},
{
"date": "2020-01-15T00:00:00Z",
"result": 0,
"name": "Yes"
},
{
"date": "2020-01-15T00:00:00Z",
"result": 0,
"name": "No"
},
{
"date": "2020-01-16T00:00:00Z",
"result": 0,
"name": "Yes"
},
{
"date": "2020-01-16T00:00:00Z",
"result": 1,
"name": "No"
}
]
这些结果的范围也不一定由日期中的开始和结束日期控制,但可以由用户指定。在这种情况下,我们需要为这些范围内的所有日期的所有选项填写零值结果。
我知道 amCharts skipEmptyPeriods 属性 (amCharts4 - skipEmptyPeriods),但我的前端工程师告诉我,这不适用于多条趋势线的情况(即第二种情况,即每条趋势线有多个选项)日期)。此外,这并不是真正的前端问题,而是会导致性能问题。
此外,我尝试将 Postgresql 的 generate_series 函数与 coalesce Postgresql - generate_series 一起使用,但无法使其适用于第二种情况。
目前我正在 Pandas 中尝试这个(我从未使用过),并解决了每个日期单个条目的第一个问题,但是,再次遇到每个日期多个条目的第二种情况:
from_date = request.query_params.get("from_date")
to_date = request.query_params.get("to_date")
# let's do some zero plotting
filtered_queryset = list(filtered_queryset)
if from_date:
from_date = datetime.strptime(from_date, "%Y-%m-%d").astimezone(pytz.UTC)
else:
from_date = filtered_queryset[0]["date"]
if to_date:
to_date = datetime.strptime(to_date, "%Y-%m-%d").astimezone(pytz.UTC)
_now = localtime(now()).astimezone(pytz.UTC)
to_date = min(to_date, _now)
else:
to_date = localtime(now()).astimezone(pytz.UTC)
pandas_freq_map = {"day": "D", "week": "W-MON", "month": "MS"}
freq = pandas_freq_map.get(request.query_params.get("frequency"))
idx = pd.date_range(from_date.date(), to_date.date(), freq=freq)
df = pd.DataFrame(list(filtered_queryset))
datetime_series = pd.to_datetime(df["date"])
datetime_index = pd.DatetimeIndex(datetime_series.values)
df = df.set_index(datetime_index)
df.drop("date", axis=1, inplace=True)
df = df.asfreq(freq)
df = df.reindex(idx, fill_value=0)
df_json = json.JSONDecoder().decode(df.to_json(date_format="iso"))
# this (result or 0) tomfoolery is bc I don't understand why pandas sometimes reindexes with null as the fill_value
prepared_response = [{"date": date, "result": (result or 0)} for date, result in df_json["result"].items()]
【问题讨论】:
-
如果您正在处理时间序列数据,您是否使用类似于TimeScale 的东西?这似乎需要一个专门的解决方案,而 Pandas 很可能是最好的临时解决方案。
-
@Jason 很遗憾,我没有时间或产品团队的批准来进行任何系统更改。然而。
-
在我看来,如果您没有时间实施必要的专门更改,您可以将此作为进一步问题的指标。 IMO,为此使用 pandas 是一种 hack,不应该被视为一个好的解决方案。
-
完全同意并且肯定会将此添加到我的证据库中,证明我们需要进行一些更改。虽然使用 Postgres 找到了解决方案。
标签: python django pandas postgresql django-rest-framework