Получить первое и последнее значение для последовательности пар между двумя столбцами фрейма данных pandas

#python #pandas

#python #pandas

Вопрос:

У меня есть фрейм данных с 3 столбцами Replaced_ID New_ID и Installation Date of New_ID .

Каждый New_ID заменяет Replaced_ID.

 Replaced_ID      New_ID             Installation Date (of New_ID)
     3             5                    16/02/2018
     5             7                    17/05/2019
     7             9                    21/06/2019
     9             11                   23/08/2020
    25             39                   16/02/2017
    39             41                   16/08/2018
  

Моя цель — получить фрейм данных, который включает в себя первую и последнюю запись последовательности. Меня волнует только первое значение Replaced_ID и последнее значение New_ID.

т.е. из вышеприведенного фрейма данных я хочу это

     Replaced_ID      New_ID             Installation Date (of New_ID)
        3              11                    23/08/2020
        25             41                    16/08/2018
  

Насколько я могу себе представить, сортировка по дате и выполнение сдвига здесь не являются решением.

Кроме того, я пытался объединить столбцы New_ID с Replaced_ID помощью, но это не так, потому что он возвращает только предыдущую последовательность.

Мне нужно найти способ получить последовательность [3,5,7,9,11] и [25,41] объединить столбцы Replaced_ID amp; New_ID для всех строк.

Я забочусь в основном о получении первого Replaced_ID значения и последнего New_ID значения, а не Installation Date потому, что я могу выполнить соединение в конце.

Есть идеи? Спасибо.

Комментарии:

1. Объедините интервалы в Replaced_ID и New_ID , а затем сопоставьте результаты New_ID с датой установки.

2. Что вы подразумеваете под объединением интервалов?

Ответ №1:

Сначала давайте создадим фрейм данных:

 import pandas as pd
import numpy as np
from io import StringIO

data = """Replaced_ID,New_ID,Installation Date (of New_ID)
3,5,16/02/2018
5,7,17/05/2019
7,9,21/06/2019
9,11,23/08/2020
25,39,16/02/2017
39,41,16/08/2018
11,14,23/09/2020
41,42,23/10/2020
"""
### note that I've added two rows to check whether it works with non-consecutive rows

### defining some short hands
r = "Replaced_ID"
n = "New_ID"
i = "Installation Date (of New_ID)"

df = pd.read_csv(StringIO(data),header=0,parse_dates=True,sep=",")
df[i] =  pd.to_datetime(df[i], )
  

А теперь для моего фактического решения:

 a = df[[r,n]].values.flatten()
### returns a flat list of r and n values which clearly show duplicate entries, i.e.:
#  [ 3  5  5  7  7  9  9 11 25 39 39 41 11 14 41 42]

### now only get values that occur once, 
#   and reshape them nicely, such that the first column gives the lowest (replaced) id,
#   and the second column gives the highest (new) id, i.e.:
#    [[ 3 14]
#     [25 42]]
u, c = np.unique( a, return_counts=True)
res = u[c == 1].reshape(2,-1)

### now filter the dataframe where "New_ID" is equal to the second column of res, i.e. [14,42]:
#   and replace the entries in "r" with the "lowest possible values" of r
dfn = df[  df[n].isin(res[:,1].tolist()) ]
# print(dfn)
dfn.loc[:][r] = res[:,0]
print(dfn)
  

Что дает:

    Replaced_ID  New_ID Installation Date (of New_ID)
6            3      14                    2020-09-23
7           25      42                    2020-10-23
  

Комментарии:

1. Это решение работает здесь, потому что максимальный идентификатор для каждой последовательности меньше минимального значения для следующей последовательности. Здесь u, c = np.unique( a, return_counts=True) выполняется сортировка. Если вы замените значения идентификатора 11 на 100, результат должен быть false . Несмотря на то, что мне нужно решение, как я описываю здесь (11 заменено на 100, я отмечу ответ @Asmus как правильный , потому что он решает проблему, которую я описал. Сейчас я думаю о том, как преодолеть эти конкретные случаи. Сгенерированные новые идентификаторы больше, чем замененные идентификаторы из других последовательностей.

Ответ №2:

Предполагая, что даты отсортированы, вы можете создать вспомогательный ряд, а затем groupby и aggregate:

 df['Installation Date (of New_ID)']=pd.to_datetime(df['Installation Date (of New_ID)'])

s = df['Replaced_ID'].ne(df['New_ID'].shift()).cumsum()
out = df.groupby(s).agg(
      {"Replaced_ID":"first","New_ID":"last","Installation Date (of New_ID)":"last"}
     )
  

 print(out)

   Replaced_ID  New_ID Installation Date (of New_ID)
1            3      11                    2020-08-23
2           25      41                    2018-08-16
  

Вспомогательный ряд s помогает различать группы, сравнивая Replaced_ID со следующим значением New_ID , и когда они не совпадают, он возвращает True . Затем с помощью series.cumsum мы возвращаем сумму по ряду для создания отдельных групп:

 print(s)

0    1
1    1
2    1
3    1
4    2
5    2
  

Комментарии:

1. Спасибо за ваш ответ. Попробовав это, я заметил 2 вещи (после сортировки дат): 1) фрейм данных out содержит только строки как есть. Он не возвращает начальное значение Replaced_ID и конечное значение New_ID. 2) выходной фрейм данных содержит все промежуточные строки. Что касается моего примера, то dataframe out содержит строки для Replaced_ID 5 и Replaced_ID 7. Обратите внимание, что сортировка по дате не означает, что строки будут ссылаться на одну и ту же последовательность пар. В результате s вернет True почти для всех строк.