Панды: объединять кадры данных и сохранять только минимальное значение, связанное с объединенными уникальными парами - PullRequest
0 голосов
/ 12 декабря 2018

У меня более сложная проблема с пандами.

Я объединяю два кадра данных в столбце V, который определяет группы.

Оба кадра данных также имеют уникальный столбец IDи Time столбец.

После объединения я вычисляю Timedelta между этими двумя столбцами и отфильтровываю отрицательные значения:

import pandas as pd

L11 = ['V1','V1','V1','V2','V2','V3','V3','V3','V3']
L12 = [1,2,3,4,5,6,7,8,9]
L13 = [pd.Timestamp("1.1.1980 12:12:12"),
       pd.Timestamp("1.1.1980 13:12:12"),
       pd.Timestamp("1.2.1980 01:12:12"),
       pd.Timestamp("1.1.1980 14:12:12"),
       pd.Timestamp("1.1.1980 16:12:12"),
       pd.Timestamp("1.1.1980 16:12:12"),
       pd.Timestamp("1.1.1980 14:12:12"),
       pd.Timestamp("1.1.1980 13:12:12"),
       pd.Timestamp("1.2.1980 10:12:12")]

L21 = ['V1','V1','V2','V3','V3','V3','V3','V3','V3']
L22 = [11,12,13,14,15,16,17,18,19]
L23 = [pd.Timestamp("1.1.1980 12:12:12"),
       pd.Timestamp("1.1.1980 13:12:12"),
       pd.Timestamp("1.1.1980 14:12:12"),
       pd.Timestamp("1.1.1980 14:12:12"),
       pd.Timestamp("1.1.1980 16:12:12"),
       pd.Timestamp("1.1.1980 18:12:12"),
       pd.Timestamp("1.1.1980 11:12:12"),
       pd.Timestamp("1.1.1980 12:12:12"),
       pd.Timestamp("1.2.1980 10:12:12")]

df1 = pd.DataFrame({'V':L11,'ID1':L12,'Time1':L13})
df2 = pd.DataFrame({'V':L21,'ID2':L22,'Time2':L23})

df = pd.merge(df1,df2,on='V')
df["Delta"] = df.Time1-df.Time2
df = df[df.Delta>pd.Timedelta(0)].copy()
df = df.drop(["Time1","Time2"],axis=1)

Дополнительно я подсчитываю, сколько записей на V -группы присутствуют в каждом фрейме данных и получают меньшее значение, которое я называю Max, потому что это будет максимально допустимое значение объединенных записей на группу.Это гарантирует, что с обеих сторон ID -значения для V -группы могут быть уникальными.

df1g = df1.groupby("V").ID1.count().reset_index().rename(columns={"ID1":"C1"})
df2g = df2.groupby("V").ID2.count().reset_index().rename(columns={"ID2":"C2"})
df12g = pd.merge(df1g,df2g,on='V')
df12g["Max"] = df12g[["C1","C2"]].min(axis=1)
df = pd.merge(df,df12g[['V','Max']],on='V')
df = df.sort_values(['V','Delta']).reset_index(drop=True)

Это мой отсортированный пример данных:

     V  ID1  ID2    Delta  Max
0   V1    2   11 01:00:00    2
1   V1    3   12 12:00:00    2
2   V1    3   11 13:00:00    2
3   V2    5   13 02:00:00    1
4   V3    8   18 01:00:00    4
5   V3    6   14 02:00:00    4
6   V3    7   18 02:00:00    4
7   V3    8   17 02:00:00    4
8   V3    7   17 03:00:00    4
9   V3    6   18 04:00:00    4
10  V3    6   17 05:00:00    4
11  V3    9   16 16:00:00    4
12  V3    9   15 18:00:00    4
13  V3    9   14 20:00:00    4
14  V3    9   18 22:00:00    4
15  V3    9   17 23:00:00    4
  • Группа V1 имеет 3 записи, но разрешена только 2
  • Группа V2 имеет 1 запись и разрешена только 1
  • Группа V3 имеет 12 записей, но разрешена только 4

Теперь мне нужно найти для каждой ID1 запись ID2 с самым низким Delta, но комбинации должны быть уникальными.

Это означает, что в строке 4 ID1 8 в паре с ID2 18 в строке 6 ID1 7 не должно быть в паре с ID2 18.

По сути, я хочу получить такой результат:

     V  ID1  ID2    Delta  Max
0   V1    2   11 01:00:00    2
1   V1    3   12 12:00:00    2
3   V2    5   13 02:00:00    1
4   V3    8   18 01:00:00    4
5   V3    6   14 02:00:00    4
8   V3    7   17 03:00:00    4
11  V3    9   16 16:00:00    4

И яя не могу понять, как этого добиться.

Простые подходы, такие как

df1 = df.drop_duplicates('ID1')
df2 = df.drop_duplicates('ID2')
result = pd.merge(df1,df2)

, очевидно, не работают должным образом.

Возможно ли даже решитьэто без перебора отсортированных строк и построения памяти из уже занятых ID2 -значений?

1 Ответ

0 голосов
/ 12 декабря 2018

Отвечая на мой собственный вопрос с помощью подхода iterrows():

После строки

df = df.sort_values(['V','Delta']).reset_index(drop=True)

это решает проблему:

df["Keep"] = False
old_V = ''
for i,row in df.iterrows():
    if row.V != old_V:
        old_V = row.V
        ID1_list = []
        ID2_list = []
    if row.ID1 not in ID1_list and row.ID2 not in ID2_list:
        df.iloc[i,5] = True
        ID1_list.append(row.ID1)
        ID2_list.append(row.ID2)
df = df[df.Keep].drop("Keep",axis=1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...