Нахождение пересечения интервалов в пандах - PullRequest
0 голосов
/ 24 апреля 2018

У меня есть два кадра данных

df_a=

     Start Stop Value
    0  0     100  0.0
    1  101   200  1.0
    2  201  1000  0.0

df_b=
       Start Stop Value
    0  0     50 0.0
    1  51   300 1.0
    2  301 1000  0.0

Я хотел бы сгенерировать DataFrame, который содержит интервалы, обозначенные Start и Stop, где значение было одинаковым в df_a и df_b. Для каждого интервала я хотел бы сохранить: если Value было одинаковым, и какое значение было в df_a и df_b. Желаемый вывод:

df_out=
  Start Stop SameValue Value_dfA Value_dfB
      0    50    1          0       0
      51   100   0          0       1
      101  200   1          1       1
      201  300   0          0       1
    [...]

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

Я нашел способ, но не уверен, что он самый эффективный. У вас есть входные данные:

import pandas as pd
dfa = pd.DataFrame({'Start': [0, 101, 201], 'Stop': [100, 200, 1000], 'Value': [0., 1., 0.]})
dfb = pd.DataFrame({'Start': [0, 51, 301], 'Stop': [50, 300, 1000], 'Value': [0., 1., 0.]})

Сначала я бы создал столбцы Start и Stop из df_out с:

df_out = pd.DataFrame({'Start': sorted(set(dfa['Start'])|set(dfb['Start'])), 
                       'Stop': sorted(set(dfa['Stop'])|set(dfb['Stop']))})

Затем, чтобы получить значение dfadfb), связанное с правильным диапазоном (Start, Stop) в столбце с именем Value_dfAValue_dfB), я бы сделал:

df_out['Value_dfA'] = df_out['Start'].apply(lambda x: dfa['Value'][dfa['Start'] <= x].iloc[-1])
df_out['Value_dfB'] = df_out['Start'].apply(lambda x: dfb['Value'][dfb['Start'] <= x].iloc[-1])

Чтобы получить столбец SameValue, выполните:

df_out['SameValue'] = df_out.apply(lambda x: 1 if x['Value_dfA'] == x['Value_dfB'] else 0,axis=1)

Если это имеет значение, вы можете изменить порядок столбцов с помощью:

df_out = df_out[['Start', 'Stop', 'SameValue', 'Value_dfA', 'Value_dfB']]

Ваш вывод будет

   Start  Stop  SameValue  Value_dfA  Value_dfB
0      0    50          1        0.0        0.0
1     51   100          0        0.0        1.0
2    101   200          1        1.0        1.0
3    201   300          0        0.0        1.0
4    301  1000          1        0.0        0.0
0 голосов
/ 25 апреля 2018

Не уверен, что это лучший способ сделать это, но вы можете reindex, join, groupby и agg, чтобы получить интервалы, например:

Расширить каждое dfтак что индексом является каждое отдельное значение диапазона (Start до Stop), используя reindex() и pad, определяя значения:

In []:
df_a_expanded = df_a.set_index('Start').reindex(range(max(df_a['Stop'])+1)).fillna(method='pad')
df_a_expanded

Out[]:
         Stop  Value
Start               
0       100.0    0.0
1       100.0    0.0
2       100.0    0.0
3       100.0    0.0
4       100.0    0.0
...
997    1000.0    0.0
998    1000.0    0.0
999    1000.0    0.0
1000   1000.0    0.0

[1001 rows x 2 columns]

In []:
df_b_expanded = df_b.set_index('Start').reindex(range(max(df_b['Stop'])+1)).fillna(method='pad')

Соедините два расширенных dfs:

In []:
df = df_a_expanded.join(df_b_expanded, lsuffix='_dfA', rsuffix='_dfB').reset_index()
df

Out[]:
      Start  Stop_dfA  Value_dfA  Stop_dfB  Value_dfB
0         0     100.0        0.0      50.0        0.0
1         1     100.0        0.0      50.0        0.0
2         2     100.0        0.0      50.0        0.0
3         3     100.0        0.0      50.0        0.0
4         4     100.0        0.0      50.0        0.0
...

Примечание: вы можете игнорировать столбцы Stop и могли бы отбросить их на предыдущем шаге.

Не существует стандартного способа groupby только для последовательных значений (à la itertools.groupby), поэтому прибегните к хаку cumsum():

In []:
groups = (df[['Value_dfA', 'Value_dfB']] != df[['Value_dfA', 'Value_dfB']].shift()).any(axis=1).cumsum()
g = df.groupby([groups, 'Value_dfA', 'Value_dfB'], as_index=False)

Теперь вы можете получить желаемый результат, агрегируя группу с min, max:

In []:
df_out = g['Start'].agg({'Start': 'min', 'Stop': 'max'})
df_out

Out[]:
   Value_dfA  Value_dfB  Start  Stop
0        0.0        0.0      0    50
1        0.0        1.0     51   100
2        1.0        1.0    101   200
3        0.0        1.0    201   300
4        0.0        0.0    301  1000

Теперь вам просто нужно добавить столбец SameValue и, при желании, упорядочить столбцы, чтобы получить точный желаемый результат:

In []:
df_out['SameValue'] = (df_out['Value_dfA'] == df_out['Value_dfB'])*1
df_out[['Start', 'Stop', 'SameValue', 'Value_dfA', 'Value_dfB']]

Out[]:
   Start  Stop  SameValue  Value_dfA  Value_dfB
0      0    50          1        0.0        0.0
1     51   100          0        0.0        1.0
2    101   200          1        1.0        1.0
3    201   300          0        0.0        1.0
4    301  1000          1        0.0        0.0

Это предполагает, что диапазоны двух кадров данных совпадают,или вам нужно будет обработать NaN s, которые вы получите с join().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...