Объединение разных строк в одну строку в зависимости от условий - PullRequest
0 голосов
/ 02 апреля 2020

У меня есть файл данных (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 строк).

...