Эффективный поиск строк в течение 31 дня по сравнению с другими строками

#pandas #pandas-groupby

#pandas #pandas-groupby

Вопрос:

У меня очень большой фрейм данных (> 5 ГБ), в котором есть строки со следующей информацией:

PatientID StudyDate Modality SliceNo Filename

Каждая строка состоит из фрагмента 3D-медицинского изображения, а для модальности у меня есть PET и CT , которые представляют собой два разных типа медицинских сканирований. Например, я мог бы:

 PatientID    StudyDate    Modality   SliceNo   Filename
000000001    2017-08-01   PT         0         XXXXX
000000001    2017-08-01   PT         1         XXXXX
...
000000001    2017-08-01   PT         100       XXXXX
000000001    2017-04-01   PT         0         XXXXX
000000001    2017-04-01   PT         1         XXXXX
...
000000001    2016-08-01   CT         0         XXXXX
000000001    2016-08-01   CT         1         XXXXX
...
000000001    2016-08-01   CT         100       XXXXX
000000001    2017-04-15   CT         0         XXXXX
000000001    2017-04-15   CT         1         XXXXX
...
000000001    2017-04-15   CT         100       XXXXX
...
000000002    2016-07-01   PT         0         XXXXX
000000002    2016-07-01   PT         1         XXXXX
...
000000002    2016-07-01   PT         100       XXXXX
000000002    2015-07-21   PT         0         XXXXX
000000002    2015-07-21   PT         1         XXXXX
...
000000002    2015-07-21   PT         100       XXXXX
000000002    2014-07-01   PT         0         XXXXX
000000002    2014-07-01   PT         1         XXXXX
...
000000002    2014-07-01   PT         100       XXXXX
000000002    2015-08-05   CT         0         XXXXX
000000002    2015-08-05   CT         1         XXXXX
...
000000002    2015-08-05   CT         100       XXXXX
  

Теперь я хотел бы найти для PT каждого пациента, которые соответствуют CT сканированию, где определяется соответствие, если оно было сделано менее чем за месяц до CT сканирования. Другие проверки могут быть отклонены (отброшены). В общем случае может быть проведено несколько КТ-сканирований и несколько ПТ-сканирований, но с каждой КТ должно быть связано одно сканирование. Например, если дата КТ-сканирования будет 2017-04-01, все КТ-сканы между 2017-03-01 и 2017-04-01 будут соответствовать требованиям.

Каков эффективный способ выбора тех томографических снимков, которые удовлетворяют условию: для этого пациента компьютерная томография проводится максимум через месяц?

Для примера и пациента 000000001 КТ 2016-08-01 не будет иметь соответствующего сканирования PT (что нормально), но сканирование PT 2017-04-01 будет выбрано, потому что сканирование CT 2017-04-15 было выполнено максимум через 31 день после сканирования PT. Итак, в этом случае отфильтровывается просмотр PT 2017-08-01. Все фрагменты ( SliceNo ) (может быть разное количество фрагментов на сканирование) с этим условием должны быть отфильтрованы. Для пациента 000000002 сохраняется только сканирование PT 2015-07-21.

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

1. Что именно означает «за месяц до»? Если CT датой является 2016-08-15 , для каких дат это допустимо PT ?

2. @JoergVanAken с 2016-07-15 по 2016-08-15 будут действительными очками!

3. Не могли бы вы предоставить еще несколько примеров данных и ожидаемый результат? У меня есть несколько идей, но я не уверен, применимы ли они.

4. @JoergVanAken попытался немного расширить его. Вкратце, все снимки PT пациента, которые не были сделаны максимум за месяц до компьютерной томографии пациента, должны быть отфильтрованы.

5. Привет, Джонас, ты видел мой ответ?

Ответ №1:

Следующий алгоритм не охватывает все случаи, но я надеюсь, что он также поможет.

Сначала мы игнорируем некоторые столбцы, потому что на самом деле нас интересуют только cols = ['PatientID', 'StudyDate', 'Modality'] . Итак, мы пишем

 df = df[cols].sort_values(cols).drop_duplicates()
  

Теперь мы определяем CT и PT периоды:

 df['Modality_'] = df.groupby(['PatientID'])['Modality'].shift(1).fillna(method='bfill')
df['Group'] = (1-df['Modality_'].eq(df['Modality'])).cumsum()
  

Затем мы вычисляем максимальную и минимальную дату в каждом периоде

 agg = df.pivot_table(index=['PatientID', 'Group'], columns=['Modality'], values=['StudyDate'], aggfunc=['max', 'min'])
  

Наконец, мы извлекаем PT и CT данные. Поскольку за PT периодом всегда следует CT точка, мы можем сдвинуть первую группу на единицу и сравнить их напрямую

 pt = agg.loc[:, ('max', 'StudyDate', 'PT')].groupby(['PatientID']).shift(1)
ct = agg.loc[:, ('min', 'StudyDate', 'CT')]
  

Мы хотим выбрать даты, в которых смещение составляет менее 30 дней:

 ok = ct - pt < pd.offsets.Day(30)
ok = ok[ok == True].to_frame()
  

Теперь мы закончили:

 print(ok.join(ct.to_frame()))
                    0 (max, StudyDate, CT)
PatientID Group                           
1         2      True           2017-04-15
2         4      True           2015-08-05

print(ok.join(pt.to_frame()))
                    0 (max, StudyDate, PT)
PatientID Group                           
1         2      True           2017-04-01
2         4      True           2015-07-21