У меня есть файл данных (ASCII), который содержит разные сообщения (содержащие разные виды данных, но каждое содержит идентификатор, CAT и дату / время сообщения), соответствующие различным категориям (CAT 1, 4, 5, 6 и 9) , Для каждого сообщения CAT 1 и для определенного идентификатора я хочу добавить данные из CAT 5 и 6 и, если возможно, 4 и 9, которые находятся в определенном временном окне сообщения CAT 1 и соответствуют одному и тому же идентификатору. В конце я хочу получить только те строки, которые имеют по крайней мере CAT 1, 5 и 6.
Сами данные не могут быть легко прочитаны с помощью pd.read_csv
, поскольку категория легкости состоит из другого количества. данных. Поэтому я читаю все в большую строку, выполняю некоторые простые манипуляции, собирая все сообщения определенной категории и объединяю их в один большой массив данных. В связи с этим каждая строка содержит одно сообщение категории и содержит только данные, соответствующие этой категории, а остальные столбцы - это NaN, см. Первые 10 строк ниже:
dt id CAT A B C
0 2020-03-25 07:45:00.402 M401ce7 9.0 NaN NaN NaN
1 2020-03-25 07:45:00.805 M401ce7 6.0 NaN NaN 241.0
2 2020-03-25 07:45:00.805 M401ce7 9.0 NaN NaN NaN
3 2020-03-25 07:45:01.000 M401ce7 1.0 54.0 NaN NaN
4 2020-03-25 07:45:01.268 M401ce7 9.0 NaN NaN NaN
5 2020-03-25 07:45:01.402 M401ce7 1.0 54.0 NaN NaN
6 2020-03-25 07:45:01.671 M401ce7 6.0 NaN NaN 241.0
7 2020-03-25 07:45:01.671 M401ce7 5.0 NaN 438.0 NaN
8 2020-03-25 07:45:01.805 M401ce7 9.0 NaN NaN NaN
9 2020-03-25 07:45:01.805 M401ce7 1.0 54.0 NaN NaN
Я написал функцию ниже, используя groupby
и две петли. Это кажется довольно медленным и было бы полезно сделать это более эффективным, поскольку я не вижу простого способа векторизации этого.
def combineCAT(df, time_window = 5):
for name, ac in df.groupby('id'):
ac = ac.copy()
for posIdx, posmsg in ac.iterrows():
if posmsg.CAT == 1:
# Get absolute time diff wrt posmsg
ac.loc[:,'timediff'] = (ac['dt'] - posmsg['dt']).dt.total_seconds().abs()
# for each cat, get minimum idx that is at least within window
idxCat = ac[ac.timediff <= time_window].groupby('CAT').timediff.idxmin()
# Check if CAT 5 and 6 are found
if idxCat.index.isin([5,6]).sum() == 2:
# Combining close messages into one
posmsg = ac.loc[idxCat.values].groupby('id').first().reset_index().iloc[0]
# Create new CAT including all cat found
CAT = [int(i) for i in idxCat.index.values.tolist()]
posmsg['CAT'] = str(CAT)[1:-1]
# logging
print("ID: {0} - Added {1} CAT: {2} to {3}".format(posmsg.id, idxCat.index.values, idxCat.values, posIdx))
# Store back into dataframe
df.loc[posIdx, :] = posmsg
else:
print("ID: {0} - No CAT 5 and/or CAT 6 messages found to combine for idx: {1}".format(posmsg.id, posIdx))
continue
else:
continue
# Retain only the usefull fields that include pos/alt and CAT 5 and 6
results = df[df.CAT.str.contains('5').fillna(False) & df.CAT.str.contains('6').fillna(False)]
return results
Для этих первых 10 строк строка 3 объединяется со строкой 7, 1 и 2, строка 5 объединяется с 7, 6 и 4, а строка 9 объединяется с 7, 6 и 8 и дает:
dt id CAT A B C
0 2020-03-25 07:45:00.402 M401ce7 9 NaN NaN NaN
1 2020-03-25 07:45:00.805 M401ce7 6 NaN NaN 241.0
2 2020-03-25 07:45:00.805 M401ce7 9 NaN NaN NaN
3 2020-03-25 07:45:01.000 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
4 2020-03-25 07:45:01.268 M401ce7 9 NaN NaN NaN
5 2020-03-25 07:45:01.402 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
6 2020-03-25 07:45:01.671 M401ce7 6 NaN NaN 241.0
7 2020-03-25 07:45:01.671 M401ce7 5 NaN 438.0 NaN
8 2020-03-25 07:45:01.805 M401ce7 9 NaN NaN NaN
9 2020-03-25 07:45:01.805 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
Последний шаг - остаться со строками 3, 5 и 9 :
dt id CAT A B C
3 2020-03-25 07:45:01.000 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
5 2020-03-25 07:45:01.402 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
9 2020-03-25 07:45:01.805 M401ce7 1, 5, 6, 9 54.0 438.0 241.0
Мой фактический код использует модуль ведения журнала, но для этого MWE я изменил ведение журнала на операторы печати. Даже без регистрации этот код все еще довольно медленный (60 с, для начального DataFrame примерно из 7800 строк).