我有一个表格,其中包含不同列中的开始和结束时间,表示天气警告到位的时间段。
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)
答案 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)