将数据框中的值与上一行中不同列中的值进行比较

时间:2018-01-18 08:47:45

标签: python pandas dataframe

我有一个表格,其中包含不同列中的开始和结束时间,表示天气警告到位的时间段。

 Warning  StartDate         End Date          Warning Issued
   1        01/05/2017 3:40   01/05/2017 4:10   Yes
   2        01/05/2017 4:10   01/05/2017 4:40   Yes
   3        02/05/2017 1:50   02/05/2017 2:30   Yes
   4        02/05/2017 2:35   02/05/2017 3:20   Yes
   5        02/05/2017 3:20   02/05/2017 4:00   Yes
   6        02/05/2017 4:00   02/05/2017 4:30   Yes
   7        03/05/2017 7:05   03/05/2017 7:50   Yes
   8        03/05/2017 7:50   03/05/2017 8:20   Yes

出于验证目的,我需要将开始和结束时间四舍五入到包含半小时(地板开始时间和上限结束时间),这很容易,但是在警告持续的情况下,这会产生重叠警告,例如警告1& 2将重叠30分钟:

Warning  FloorStartDate       CeilEnd Date      Warning Issued
   1     01/05/2017 3:30      01/05/2017 4:30   Yes
   2     01/05/2017 4:00      01/05/2017 5:00   Yes

然而,如果我只是绕过时间,我将失去半小时的警告不连续的时间段(例如半小时时段'1.30 -2pm'警告3开始时会丢失)。

我想找到一个解决方案,我可以将第2行的开始日期与第1行的结束日期进行比较,如果它们相等,那么我将对时间进行舍入,如果不是,我会将时间设置为最小值。该表如下所示:

Warning   StartDate         End Date     WarnIss GroomStart     GroomEnd
    1  01/05/2017 3:40   01/05/2017 4:10  Yes 01/05/2017 3:30  01/05/2017 4:00
    2  01/05/2017 4:10   01/05/2017 4:40  Yes 01/05/2017 4:00  01/05/2017 5:00
    3  02/05/2017 1:50   02/05/2017 2:30  Yes 02/05/2017 1:30  02/05/2017 2:30
    4  02/05/2017 2:35   02/05/2017 3:20  Yes 02/05/2017 2:30  02/05/2017 3:30
    5  02/05/2017 3:20   02/05/2017 4:00  Yes 02/05/2017 3:30  02/05/2017 4:00
    6  02/05/2017 4:00   02/05/2017 4:30  Yes 02/05/2017 4:00  02/05/2017 4:30
    7  03/05/2017 7:05   03/05/2017 7:50  Yes 03/05/2017 7:00  03/05/2017 8:00
    8  03/05/2017 7:50   03/05/2017 8:20  Yes 03/05/2017 8:00  03/05/2017 8:30

我搜索过任何答案的高低,我能得到的最接近的是this。此代码比较它之前的行,但仅在同一列中。我试图重新调整代码(下面),但它只返回else值(我怀疑因为我没有正确迭代或者没有正确定位值)。

任何人都可以建议这是否可行,如果是,请指出我的代码失败的地方或为我提供替代解决方案?

import pandas as pd

def FormatWarning(inputcsv,outputcsv):
    df = pd.DataFrame()
    df = pd.read_csv(inputcsv,\
                     index_col=None, \
                     names=['Warning','DateStart','DateEnd',\
                            'WarningIssued'],\
                     sep=',',\)
    df["DateStart"]= pd.to_datetime(df["DateStart"],format='%d-%m-%Y %H:%M')
    df["DateEnd"]= pd.to_datetime(df["DateEnd"],format='%d-%m-%Y %H:%M')

for i in range(2, len(df)):
    if df.loc[i,'DateStart']== df.loc[i-1,'DateEnd']:
        df["EditDTS"]= df["DateStart"].dt.round('30T')
    else:
        df["EditDTS"]= df["DateStart"].dt.floor('30T')

for i in range(1, len(df)-1):
    if df.loc[i,"DateEnd"]==df.loc[i+1,"DateStart"]:
        df["EditDTE"]=df["DateEnd"].dt.round('30T')
    else:
        df["EditDTE"]=df["DateEnd"].dt.ceil('30T')       
df.to_csv(outputcsv, index=False)

1 个答案:

答案 0 :(得分:0)

我以稍微不同的方式接近它 - 我使用.apply(axis=1)分别访问每一行,但然后将该行和主DF传递给定义的方法。传递两者的基本原理是我可以访问每一行的.name属性,而不是遍历循环。在这样的矢量化操作中工作可以更快,并且我发现更明确。

df = pd.DataFrame(
    [
        ["1","01/05/2017 3:40","01/05/2017 4:10","Yes"],
        ["2","01/05/2017 4:10","01/05/2017 4:40","Yes"],
        ["3","02/05/2017 1:50","02/05/2017 2:30","Yes"],
        ["4","02/05/2017 2:35","02/05/2017 3:20","Yes"],
        ["5","02/05/2017 3:20","02/05/2017 4:00","Yes"],
        ["6","02/05/2017 4:00","02/05/2017 4:30","Yes"],
        ["7","03/05/2017 7:05","03/05/2017 7:50","Yes"],
        ["8","03/05/2017 7:50","03/05/2017 8:20","Yes"],
    ], columns=["Warning","DateStart","DateEnd","Warning Issued"]
)
df["DateEnd"] = pd.to_datetime(df["DateEnd"])
df["DateStart"] = pd.to_datetime(df["DateStart"])


def start_process(row, df):
    try:
        if row["DateStart"] == df.loc[(row.name - 1), "DateEnd"]:
            return row["DateStart"].round('30T')
        else:
            return row["DateStart"].floor('30T')
    except KeyError:
        # Either the first or last entry, so just return the original
        return row["DateStart"]

def end_process(row, df):
    try:
        if row["DateEnd"] == df.loc[(row.name + 1), "DateStart"]:
            return row["DateEnd"].round('30T')
        else:
            return row["DateEnd"].ceil('30T') 
    except KeyError:
        # Either the first or last entry, so just return the original
        return row["DateEnd"]

df["EditDTS"] = df.apply(lambda x: start_process(x, df), axis=1)
df["EditDTE"] = df.apply(lambda x: end_process(x, df), axis=1)
print(df)

结果:

  Warning           DateStart             DateEnd Warning Issued  \
0       1 2017-01-05 03:40:00 2017-01-05 04:10:00            Yes
1       2 2017-01-05 04:10:00 2017-01-05 04:40:00            Yes
2       3 2017-02-05 01:50:00 2017-02-05 02:30:00            Yes
3       4 2017-02-05 02:35:00 2017-02-05 03:20:00            Yes
4       5 2017-02-05 03:20:00 2017-02-05 04:00:00            Yes
5       6 2017-02-05 04:00:00 2017-02-05 04:30:00            Yes
6       7 2017-03-05 07:05:00 2017-03-05 07:50:00            Yes
7       8 2017-03-05 07:50:00 2017-03-05 08:20:00            Yes

              EditDTS             EditDTE
0 2017-01-05 03:40:00 2017-01-05 04:00:00
1 2017-01-05 04:00:00 2017-01-05 05:00:00
2 2017-02-05 01:30:00 2017-02-05 02:30:00
3 2017-02-05 02:30:00 2017-02-05 03:30:00
4 2017-02-05 03:30:00 2017-02-05 04:00:00
5 2017-02-05 04:00:00 2017-02-05 04:30:00
6 2017-03-05 07:00:00 2017-03-05 08:00:00
7 2017-03-05 08:00:00 2017-03-05 08:20:00

注意:我使用的是Pandas 0.22和Python 3.5,因此,如果任何代码对您的设置无效,可能是原因。

编辑19/01/2018

回答为什么原始代码不起作用的问题:我当时没有注意到原始代码的错误,因为我使用{{1}将其重构为行方法}。但是,我现在看到您的作业看起来像是问题。

对于每个循环,您将在整个DF列而不是特定行上执行操作:

.apply()

您只需要将for i in range(2, len(df)): # Your lookup was correct, using the row indexers... if df.loc[i,'DateStart']== df.loc[i-1,'DateEnd']: # but the assignment is being made on the whole column, # so each iteration is potentially undoing the work of the last iteration. df["EditDTS"]= df["DateStart"].dt.round('30T') else: df["EditDTS"]= df["DateStart"].dt.floor('30T') 语句中的相同索引器用于以下操作:

if

所以 - 重构是没有必要的,但是通过分解步骤它可能更清楚地调试。 (灵感来自The Zen of Python