Как добавить временную метку между двумя конкретными временными метками промышленного оборудования - Python Pandas

Я работаю над наличием конкретного оборудования. После нескольких обработок данных у меня наконец-то появилось время в часах недоступности оборудования.

Анализируя мои результаты, я обнаружил, что у меня недоступность 41 час в день ... невозможно.

Я наконец нашел проблему, вот два примера:

Date              Unavailability
2019-10-25                 41.47

Flag                   Timestamp
begin     2019-10-25 16:35:22.48
end       2019-10-27 09:50:31.71

В примере 1 у меня есть начало, которое начинается 10-25, но заканчивается 27 ... в то время как компания закрылась в 5 вечера, поэтому они больше не производят. Но проблема не была решена до 27 в 9:50.

Я нашел решение, но мне все еще крайне не хватает опыта работы с метками времени в пандах.

Бизнес открывается в 7:00 каждый день и закрывается в 17:00.

Строка с флагом «конец» не имеет той же даты, что и строка из «начала» ранее. Если мы изменим число дней, 27-25 = 2, поэтому мы должны добавить:

Flag                   Timestamp
end       2019-10-25 17:00:00.00
begin     2019-10-26 07:00:00.00
end       2019-10-26 17:00:00.00
begin     2019-10-27 07:00:00.00

Если мы вернемся к примеру 1 с concatenate, это даст:

Date              Unavailability
2019-10-25                 25min
2019-10-26                   10h 
2019-10-27               2h50min 

Flag                   Timestamp
begin     2019-10-25 16:35:22.48
end       2019-10-25 17:00:00.00
begin     2019-10-26 07:00:00.00
end       2019-10-26 17:00:00.00
begin     2019-10-27 07:00:00.00
end       2019-10-27 09:50:31.71

С другим примером:

Date              Unavailability
2019-10-21                   10h

Flag                   Timestamp
begin     2019-10-21 15:30:22.48
end       2019-10-22 08:30:31.71

То же самое, сигнал тревоги начинается 2019-10-21 и заканчивается 2019-10-22, поэтому недоступность 10:00 не является хорошей. Потому что вы должны учитывать рабочее время. мы делаем разницу дат: 22-21 = 1, поэтому мы должны добавить:

Flag                   Timestamp
end          2019-10-21 17:00:00
begin        2019-10-22 07:00:00

с конкатенацией это дает:

Date              Unavailability
2019-10-21                  1h30
2019-10-21                  1h30

Flag                   Timestamp
begin     2019-10-21 15:30:22.48
end       2019-10-21 17:00:00.00
begin     2019-10-22 07:00:00.00
end       2019-10-22 08:30:31.71

эта вставка выполняется только в том случае, если между началом и концом имеется переполнение, поскольку после каждого начала следует конец одной и той же даты.

Образец моего фрейма данных:

Flag                     Timestamp
begin   2019-10-25 09:39:39.914889
end     2019-10-25 09:41:09.103102
begin   2019-10-25 10:39:58.352073
end     2019-10-25 10:40:06.266782
begin   2019-10-25 16:35:22.485574
end     2019-10-27 09:50:31.713192
begin   2019-10-28 14:04:33.095633
end     2019-10-28 14:05:07.639344
begin   2019-10-28 14:13:07.924966
end     2019-10-28 14:13:08.888890

Спасибо за ваше время !

Доказательство :

start   Tranc   dayofMonth  lapse   TrancRecalibration
0   2019-10-25 09:39:39.914889  begin   25.0    0.0     1
1   2019-10-25 09:41:09.103102  end     25.0    0.0     2
2   2019-10-25 10:39:58.352073  begin   25.0    0.0     1
3   2019-10-25 10:40:06.266782  end     25.0    0.0     2
4   2019-10-25 16:35:22.485574  begin   25.0    0.0     1
5   2019-10-25 17:00:22.485574  end     NaN     0.0     2
7   2019-10-26 07:00:39.914889  begin   NaN     1.0     1
6   2019-10-26 17:00:39.914889  end     NaN     1.0     2
11  2019-10-27 07:00:39.914889  begin   NaN     1.0     1
8   2019-10-27 08:00:31.713192  begin   NaN     0.0     1
9   2019-10-27 09:50:31.713192  end     27.0    0.0     2
10  2019-10-27 17:00:39.914889  end     NaN     1.0     2
15  2019-10-28 07:00:39.914889  begin   NaN     1.0     1
12  2019-10-28 14:04:33.095633  begin   28.0    0.0     1
14  2019-10-28 14:05:07.639344  end     28.0    0.0     2
13  2019-10-28 14:13:07.924966  begin   28.0    0.0     1
16  2019-10-28 14:13:08.888890  end     28.0    0.0     2
17  2019-10-28 17:00:39.914889  end     NaN     1.0     

Действительно здорово, что ты сделал, мне так и не удалось ...

Еще один последний момент, если вы позволите это:

это результат с изменением с 4:55 вечера до 7:00 утра.

Как мы можем заметить, у нас есть:

начало 7:00 утра начало 8:00 утра конец 9:50 утра конец 5:00 вечера начало 7:00 утра начало 2:04 вечера. , конец 2:13 вечера конец 5:00 вечера

Для расчета по отношению к моим сигналам тревоги. У меня должен быть альтернативный конец начала каждый раз. поэтому, если у меня есть два начала, которые следуют, я хотел бы удалить 7:00 утра, и если у меня есть два конца, которые следуют, я хотел бы удалить 5:00 вечера, пожалуйста.

Всего 2 ответа


Я сначала преобразовал бы df, чтобы иметь отдельные столбцы начала и конца:

df1['begin'] = df[df['Flag']=='begin']['Timestamp']
df1['end'] = df[df['Flag']=='end']['Timestamp']

затем рассчитайте время до end и время до 17:00, возьмите min () из двух:

df1['time_to_end'] = df1['end'] - df1['begin']
df1['time_to_17'] = pd.Timestamp(year = df1['begin'].dt.year, month = df1['begin'].dt.month, day = df1['begin'].dt.day ,hour=17, minute=0) - df1['begin']
df1['Unavailibility'] = df1[['time_to_end','time_to_17']].min(1)

Вам нужно сгруппировать дату:

df1[['begin','Unavailibility']].set_index('begin').groupby(pd.Grouper(freq='D')).sum()

Хотел бы сделать это кратким, но должен убедиться, что вы понимаете, как я это сделал. Давай узнаем, что мы тебя не так поняли.

Dataframe

df=pd.DataFrame({'Tranc':['begin', 'end', 'begin', 'end', 'begin', 'end', 'begin', 'end', 'begin', 'end'], 'lapse':[-1.0, -1.0, -42.0, -15.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0],'start':[񟭓-10-25 09:39:39.914889', 񟭓-10-25 09:41:09.103102', 񟭓-10-25 10:39:58.352073', 񟭓-10-25 10:40:06.266782', 񟭓-10-25 16:35:22.485574', 񟭓-10-27 09:50:31.713192', 񟭓-10-28 14:04:33.095633', 񟭓-10-28 14:05:07.639344', ' 2019-10-28 14:13:07.924966', 񟭓-10-28 14:13:08.888890']})

Приведите дату к дате и установите дату начала как индекс

df['start']=pd.to_datetime(df['start'])
df.set_index('start', inplace=True)

Рассчитайте разницу во времени, чтобы определить разницу в 1 день

df['dayofMonth']=df.index.day
df['lapse']=df.dayofMonth.diff().fillna(0)
df.reset_index(inplace=True)

Вставьте строки, где есть разница в днях

k = df.index[df.lapse >=1]
insertdata= pd.DataFrame({'lapse':[-1]})
df2= pd.DataFrame(insertdata.values.tolist() * len(k), 
                   columns=insertdata.columns, index=k-1)
res = pd.concat([df, df2]).sort_index().reset_index(drop=True)

Вперед вставка обратной засыпки, чтобы мы решили проблемы с существующими датами и подготовили df для заполнения пропущенных дат

res.Tranc=res.Tranc.bfill()
res.start=res.start.ffill()
res.sort_values(by='Tranc', ascending=True)
res

Заданы дни запроса и маска

m=(res['lapse']==-1.0) & (res['Tranc']=='end')
mask=(res['lapse']==-1.0) & (res['Tranc']=='begin')

Изменить вставленные конечные часы начала

res.loc[m, 'start']= res.loc[m, 'start'].apply(lambda x: x.replace(hour=17, minute=0))
res.loc[mask, 'start']= res.loc[mask, 'start'].apply(lambda x: x.replace(hour=8, minute=0))
res.drop(columns=['lapse'], inplace=True)

res.sort_values(by='start')

Часть вторая Вставьте недостающие даты и при необходимости укажите их. Обратите внимание, что я решил сделать время начала 7:00 и время окончания 17:00 чтобы упростить сортировку, а также с учетом того, что мы только заполняем даты. Вы можете изменить, если требуется.

Преобразуйте res и отдохните для следующего этапа.

res2=res
res2
res2.set_index(res2['start'], inplace=True)
res2.drop(columns=['start'],inplace=True)
#df['dates']=df.index.date
res2.reset_index(inplace=True)
res2.set_index('start', inplace=True)
res2['lapse']=0
res2

Вставьте пропущенные даты, сохраняя дубликаты

s = pd.Series(np.nan, index=pd.date_range(res2.index.min(), res2.index.max(), freq='D'))
df2=pd.concat([res2,s[~s.index.isin(res2.index)]]).sort_index()
df2.lapse.fillna(1, inplace=True)#Fill lapse with 1, so that can use that in df.repeat to replicate rows
df2.drop(columns=0, inplace=True)#default column, get rid of it
df2

Для вставленных строк скопируйте их. Я использую целое число в промежутке, чтобы указать, сколько раз можно реплицировать каждый индекс, и сохранить реплики в новом df3.

df3=df2.loc[df2.index.repeat(df2.lapse)]
df3

Concat df2 и df3 в новом временном df res3

res3 = pd.concat([df2, df3]).sort_index().reset_index(drop=False)
res3.rename(columns={'index':'start'}, inplace=True)
res3

Введите новый столбец, в который я вставляю шаблон 1 2, 1; начало и 2; конец для последующего использования

res3['TrancRecalibration']=0
np.put(res3['TrancRecalibration'], np.arange(len(res3)), [1,2])
res3

Выберите все строки с помощью Tranc, что означает, что они уже были установлены на этапе 1 в df4 и сбросьте индекс, чтобы мы могли использовать его для последующего объединения

df4=res3[res3['Tranc'].notna()]
df4.set_index('start', inplace=True)
df4['Date']=df4.index.date
df4.reset_index(inplace=True)
df4.set_index('Date', inplace=True)
df4

Выберите недавно вставленные недостающие даты

df5=res3[res3['Tranc'].isna()]

df5['TrancRecalibration']=0
np.put(df5['TrancRecalibration'], np.arange(len(df5)), [1,2])
df5

маска df5 для изоляции с помощью TrancRecalibration (1 или 2), обозначающая начало или конец и приписывающая Tranc

n=df5['TrancRecalibration']==1
l=df5['TrancRecalibration']==2
df5['Tranc']=np.where(n,'begin','end')

Установите время начала и окончания 7:00 и 17:00 соответственно

df5.loc[n, 'start']= df5.loc[n, 'start'].apply(lambda x: x.replace(hour=7, minute=00))
df5.loc[l, 'start']= df5.loc[l, 'start'].apply(lambda x: x.replace(hour=17, minute=0))

сбросить индекс для df5 чтобы его можно было df4 с df4

df5.set_index('start', inplace=True)
df5['Date']=df5.index.date
df5.reset_index(inplace=True)
df5.set_index('Date', inplace=True)
df5

Concat df4 и df5 в результате

result = pd.concat([df4, df5]).sort_index().reset_index(drop=True).sort_values(by='start')
result

Выход

введите описание изображения здесь

После вашего запроса ограничить время начала до 7 утра в случае вставки строки. Вы можете использовать следующее, чтобы отбросить последовательные begin begin end end подряд

Определить шаблоны

pattern1=['begin', 'begin']

Отбросьте первые появления в последовательности паттернов ['begin', 'begin']

p1=(result.Tranc==pattern1[0])&(result["Tranc"].shift(-1)==pattern1[1])
# p1 indicates the first begin in a pettern of begin begin
result2=result[~p1]# drops the first begin in a pattern of begin begin

Повторите вышеуказанный шаг, но на этот раз сбросьте последнюю запись в шаблоне последовательности ['end', 'end']

pattern2=['end', 'end']
p2=(result2.Tranc==pattern2[1])&(result2["Tranc"].shift(1)==pattern2[0])

result2[~p2].sort_values(by='start')

Окончательный вывод

введите описание изображения здесь

Отсюда, продолжить и проанализировать вашу недоступность:


Есть идеи?

10000