【问题标题】:Python time series dataframe query inside for-loopfor循环内的Python时间序列数据框查询
【发布时间】:2021-02-08 07:02:40
【问题描述】:

我有一个 pandas 数据框 df,我使用 for 循环对其进行查询,该循环通过考虑 Status 列的虚拟变量来捕获事件。逻辑是当Status0变为1时,它标志着1的时间戳为事件的开始,当Status1变为0时,它标志着0 的时间戳作为事件的结束。

df 看起来像这样:

        ID  Timestamp               Value   Status
103177  64  2010-09-21 23:13:21.090 21.5    1.0
252019  64  2010-09-22 00:44:14.890 21.5    1.0
271381  64  2010-09-22 00:44:15.890 21.5    0.0
268939  64  2010-09-22 00:44:17.890 23.0    0.0
259875  64  2010-09-22 00:44:18.440 23.0    1.0
18870   64  2010-09-22 00:44:19.890 24.5    1.0
205910  64  2010-09-22 00:44:23.440 24.5    0.0
103865  64  2010-09-22 01:04:33.440 23.5    0.0
152281  64  2010-09-22 01:27:01.790 22.5    0.0
138988  64  2010-09-22 02:18:52.850 21.5    0.0
             ...

可重现的例子:

from pandas import *
from numpy import nan

df = pd.DataFrame({'ID': {103177: 64,
  252019: 64,
  271381: 64,
  268939: 64,
  259875: 64,
  18870: 64,
  205910: 64,
  103865: 64,
  152281: 64,
  138988: 64},
 'Timestamp': {103177: Timestamp('2010-09-21 23:13:21.090000'),
  252019: Timestamp('2010-09-22 00:44:14.890000'),
  271381: Timestamp('2010-09-22 00:44:15.890000'),
  268939: Timestamp('2010-09-22 00:44:17.890000'),
  259875: Timestamp('2010-09-22 00:44:18.440000'),
  18870: Timestamp('2010-09-22 00:44:19.890000'),
  205910: Timestamp('2010-09-22 00:44:23.440000'),
  103865: Timestamp('2010-09-22 01:04:33.440000'),
  152281: Timestamp('2010-09-22 01:27:01.790000'),
  138988: Timestamp('2010-09-22 02:18:52.850000')},
 'Value': {103177: 21.5,
  252019: 21.5,
  271381: 21.5,
  268939: 23.0,
  259875: 23.0,
  18870: 24.5,
  205910: 24.5,
  103865: 23.5,
  152281: 22.5,
  138988: 21.5},
 'Status': {103177: 1.0,
  252019: 1.0,
  271381: 0.0,
  268939: 0.0,
  259875: 1.0,
  18870: 1.0,
  205910: 0.0,
  103865: 0.0,
  152281: 0.0,
  138988: 0.0}})

df

查询for循环:

def my_func1(df):
    
    dataframe = []
    Start_time = []
    StartValue   = []
    End_time = []
    End_Value = []
    StartStatus = []
    End_Status = []
    ID = []

    state = 0

    for i in range(1, len(df.index)):

        if state == 0:

            if (df.loc[i, 'Status'] > 0):

                Start_time = df.loc[i, 'Timestamp']
                StartValue = df.loc[i, 'Value']  
                StartStatus = df.loc[i, 'Status']
                ID = df.loc[i, 'ID']

                state = 1

            else:
                continue

        elif state == 1:

            if (df.loc[i, 'Status'] == 0):
            
                End_time = df.loc[i, 'Timestamp']
                End_Value = df.loc[i, 'Value']
                End_Status = df.loc[i, 'Status'] 
                
                state = 0

            else:
                continue


            dataframe.append([ ID, Start_time, StartValue, StartStatus, End_time, End_Value, End_Status])       

        else:
            continue

    # Dataframe 
    output_table = pd.DataFrame(dataframe, columns= ["ID", "StartTime", "StartValue", "StartStatus",  "EndTime", "EndValue", "EndStatus"])

    return output_table

输出表格式如下(只是一个例子,与上面的数据不同):

    ID  StartTime                StartValue StartStatus EndTime                 EndValue EndStatus
0   64  2010-09-22 00:44:14.890  21.5       1.0         2010-09-22 00:44:15.890 21.5     0.0
1   64  2010-09-22 00:44:18.440  23.0       1.0         2010-09-22 00:44:23.440 24.5     0.0
2   64  2010-09-23 00:06:48.970  100.0      1.0         2010-09-23 00:06:52.970 100.0    0.0
3   64  2010-09-23 00:07:14.970  100.0      1.0         2010-09-23 00:07:23.970 100.0    0.0
4   64  2010-09-23 02:07:52.990  100.0      1.0         2010-09-23 02:08:13.990 100.0    0.0

现在我想使用output_table 中的每一行查看原始数据框df 内的每个事件,其中每个事件可以通过在@987654341 之间过滤Timestamp 来找到@ 和 EndTime。如何将我自己定义的另一个函数my_func2 应用于df 中每个单独事件的数据? (my_func2 应该在output_table 中创建一个标签,通过考虑Value 列来表示每个事件是否满足某些条件。)


编辑:

更具体地说,my_func2 测量连续时间间隔,其中Value 保持在30 以上至少 10 秒。然后我想找到my_func1my_func2的重叠,最终计算每个事件在这个时间间隔内的median值。

我想我可以通过比较两个事件的起点和终点的时间戳(由两个函数计算)来计算重叠。我需要帮助的是如何在df 中找到原始数据以计算每个单独事件的 median 值?任何想法都值得赞赏。

【问题讨论】:

  • 能否提供更方便的数据格式?您可以查看How to make good reproducible pandas examples 了解更多信息。
  • 请说明实际解决方案的要求,并直接举例说明。避免使用XY problem,因为您提出的方案不需要解决您的问题。
  • 嗨@AMC,请查看已编辑的问题。
  • 关于“0 -> 1 表示开始”这句话——哪一条记录标志着开始?你的“中间”是否包括起始行?最后也会出现同样的问题。请举例说明或至少提供明确和明确的规则。
  • 嗨@BillHuang,它标志着Status 开始转1(从0 开始),并将结束标记为Status 转0(从1 开始)。是的“介于”包括开始值和结束值

标签: python pandas for-loop


【解决方案1】:

您不必在找到区间后提取行。 可以同时提取区间以及其间的行,而且从效率上来说应该是这样。

解决方案

有效提取的关键是通过diff-cumsum 技巧捕获事件连续性。您的情况有点特殊,因为连胜之后的下一个元素也算作连胜。这反映在代码中标志is_ev的定义中。

执行diff-cumsum 技巧后,事件条纹和非事件条纹将交错。对 2 的右模由初始 Status 确定。

df["diff"] = df["Status"].diff()
df["is_start"] = df["diff"] == 1
df["is_end"] = df["diff"] == -1
df["is_ev"] = (df["Status"] == 1) | df["is_end"]
df["ev_number"] = df["is_ev"].diff().cumsum()
df["ev_number"].iat[0] = 0

# output 1: interval marks
df_start_end = df[df["is_start"] | df["is_end"]]

# output 2: records between (inclusive) events in chronological ordering
# - different events can be subset using
#   df_records[df_records["ev_number"] == N]
# - Why set the modulus:
#   If df begins with Status = 1, event records will have even df["ev_number"], namely modulus = 0 against divisor 2
#   If df begins with Status = 0, event records will have odd df["ev_number"], so modulus = 1
modulus = 0 if df["Status"].iat[0] == 1 else 1
df_records = df[df["ev_number"] % 2 == modulus]

结果

print(df_start_end.drop(columns=["diff", "is_ev"]))

        ID               Timestamp  Value  Status  is_start  is_end ev_number
271381  64 2010-09-22 00:44:15.890   21.5     0.0     False    True         0
259875  64 2010-09-22 00:44:18.440   23.0     1.0      True   False         2
205910  64 2010-09-22 00:44:23.440   24.5     0.0     False    True         2

print(df_records.drop(columns=["diff", "is_start", "is_end", "is_ev"]))

        ID               Timestamp  Value  Status ev_number
103177  64 2010-09-21 23:13:21.090   21.5     1.0         0
252019  64 2010-09-22 00:44:14.890   21.5     1.0         0
271381  64 2010-09-22 00:44:15.890   21.5     0.0         0
259875  64 2010-09-22 00:44:18.440   23.0     1.0         2
18870   64 2010-09-22 00:44:19.890   24.5     1.0         2
205910  64 2010-09-22 00:44:23.440   24.5     0.0         2

进一步的步骤

1。间隔列表(开始时间、结束时间)

df_out = df_start_end.loc[df_start_end["is_start"], ["Timestamp", "ev_number"]]\
    .merge(df_start_end.loc[df_start_end["is_end"], ["ID", "Timestamp", "ev_number"]], how="outer", on="ev_number")\
    .rename(columns={"Timestamp_x": "StartTime", "Timestamp_y": "EndTime"})\
    .sort_values("EndTime")\
    .reset_index(drop=True)

# add the first record as first interval StartTime when needed
if modulus == 0:
    df_out["StartTime"].iat[0] = df["Timestamp"].iat[0]

print(df_out[["ID","StartTime","EndTime","ev_number"]])
   ID               StartTime                 EndTime  ev_number
0  64 2010-09-21 23:13:21.090 2010-09-22 00:44:15.890        0.0
1  64 2010-09-22 00:44:18.440 2010-09-22 00:44:23.440        2.0

2。在df右边追加(StartTime, EndTime)

这可以通过匹配ev_number 轻松完成。

df_with_interval = df\
    .merge(df_start_end.loc[df_start_end["is_start"], ["Timestamp", "ev_number"]], on="ev_number", suffixes=("", "_1"))\
    .merge(df_start_end.loc[df_start_end["is_end"], ["Timestamp", "ev_number"]], on="ev_number", suffixes=("", "_2"))\
    .rename(columns={"Timestamp_1": "StartTime", "Timestamp_2": "EndTime"})\
    .sort_values("Timestamp")

print(df_with_interval)
   ID               Timestamp  ...               StartTime                 EndTime
0  64 2010-09-22 00:44:18.440  ... 2010-09-22 00:44:18.440 2010-09-22 00:44:23.440
1  64 2010-09-22 00:44:19.890  ... 2010-09-22 00:44:18.440 2010-09-22 00:44:23.440
2  64 2010-09-22 00:44:23.440  ... 2010-09-22 00:44:18.440 2010-09-22 00:44:23.440

但是,我不会更进一步,因为我不知道数据的真实用例。我相信到目前为止,中心问题已经解决了。

注意这是why you shouldn't assign the means of solutions when you ask questions 上的示例。

【讨论】:

  • 感谢您的解决方案。你能详细解释一下# output 2吗?附言我认为合并没有按预期工作,因为它创建了重复的列。
  • 这两步的目的是什么:'modulus = 0 if df["Status"].iat[0] == 1 else 1 df_records = df[df["ev_number"] % 2 = = 模数]'?
  • 我可以按一些列分组吗,比如df["ev_number"] = df.groupby(['A', 'B'])["is_ev"].diff().cumsum()?但我得到了相同的连续ev_number
  • 这是另一个基于不同数据集的问题。您可以直接发布另一个问题。另请注意,SO 是一个问答网站,而不是讨论论坛,因此不鼓励与原始问题或此答案无关的 cmets。如果您还有其他好的后续问题,请单独询问。这在 SO 上并不少见。注:我删除了我的其他 cmets,因为它们现在已集成到帖子中。您也可以这样做以减少对其他潜在读者的干扰。
  • 为什么is_ev 列的TrueFalse 值在cumsum 中不相互抵消?
猜你喜欢
  • 1970-01-01
  • 2020-06-25
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
相关资源
最近更新 更多