Фильтрация DataFrame для данных идентификаторов, значения которых со временем уменьшаются - PullRequest
0 голосов
/ 04 июля 2018

У меня есть большой набор данных временных рядов результатов пациентов. Один пациент имеет один идентификатор с различными значениями результата. Данные отсортированы по дате и идентификатору. Я хочу смотреть только на пациентов, чьи значения строго снижаются с течением времени. Например, пациент x имеет значения результата 5, 3, 2, 1 будет истинным. Однако 5,3,6,7,1 будет ложным.

Пример данных:

import pandas as pd
df = pd.read_excel(...)
print(df.head())
  PSA   PSAdate‎  PatientID    ...          datefirstinject  ADTkey  RT_PSAbin
0  2.40 2007-06-26      11448    ...      2006-08-05 00:00:00       1         14
1  0.04 2007-09-26      11448    ...      2006-08-05 00:00:00       1         15
2  2.30 2008-01-14      11448    ...      2006-08-05 00:00:00       1         17
3  4.03 2008-04-16      11448    ...      2006-08-05 00:00:00       1         18
4  6.70 2008-07-01      11448    ...      2006-08-05 00:00:00       1         19

Так что для этого примера я хочу видеть только строки с PatientID с, для которых значение PSA со временем уменьшается.

groupID = df.groupby('PatientID')
def is_desc(d):
    for i in range(len(d) - 1):
        if d[i] > d[i+1]:
            return False
    return True

x = groupID.PSA.apply(is_desc)
df['is_desc'] = groupID.PSA.transform(is_desc)
#patients whose PSA values is decreasing overtime.
df1 = df[df['is_desc']]

Я получаю:

KeyError: 0

Я полагаю, что цикл не может пройти через сгруппированные значения, так как ему требуется массив для поиска «диапазона». Есть идеи по редактированию цикла?

Ответы [ 3 ]

0 голосов
/ 04 июля 2018

Это должно решить ваш вопрос, интерпретируя «уменьшение» как монотонное уменьшение:

import pandas as pd

d = {"PatientID": [1,1,1,1,2,2,2,2],
     "PSAdate": [2010,2011,2012,2013,2010,2011,2012,2013],
     "PSA": [5,3,2,1,5,3,4,5]}

# Sorts by id and date
df = pd.DataFrame(data=d).sort_values(['PatientID', 'PSAdate'])

# Computes change and max(change) between sequential PSA's
df["change"] = df.groupby('PatientID')["PSA"].diff()
df["max_change"] = df.groupby('PatientID')['change'].transform('max')

# Considers only patients whose PSA are monotonic decreasing
df = df.loc[df["max_change"] <= 0]

print(df)

   PatientID  PSAdate  PSA  change  max_change
0          1     2010    5     NaN        -1.0
1          1     2011    3    -2.0        -1.0
2          1     2012    2    -1.0        -1.0
3          1     2013    1    -1.0        -1.0

Примечание: учитывать только строго монотонно убывающий PSA, изменить окончательное loc условие на < 0

0 голосов
/ 04 июля 2018

TL; DR

# (see is_desc function definition below)
df['is_desc'] = df.groupby('PationtID').PSA.transform(is_desc)
df[df['is_desc']]

Объяснение

Давайте использовать очень простой набор данных:

df = pd.DataFrame({'id': [1,2,1,3,3,1], 'res': [3,1,2,1,5,1]})

Содержит только идентификатор и один столбец значений (и имеет индекс, автоматически назначаемый из панд).

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

Итак, сначала давайте определим функцию, которая проверяет, уменьшаются ли значения:

def is_desc(d):
first = True
for i in d:
    if first:
        first = False
    else:
        if i >= last:
            return False
    last = i
return True

(да, возможно, это можно было бы сделать более элегантно, для лучшей реализации вы можете выполнить поиск в Интернете)

теперь мы группируем по id :

gb = df.groupby('id')

и применяют функцию :

x = gb.res.apply(is_desc)

x теперь содержит это Series:

id
1     True
2     True
3    False
dtype: bool

так что теперь, если вы хотите отфильтровать это, вы можете просто сделать это:

x[x].index

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

list(x[x].index)

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

[1, 2]

Но если вы хотите также иметь все исходные данные для всех выбранных идентификаторов, сделайте это так:

df['is_desc'] = gb.res.transform(is_des)

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

   id  res  is_desc
0   1    3     True
1   2    1     True
2   1    2     True
3   3    1    False
4   3    5    False
5   1    1     True

Теперь вы можете очень легко отфильтровать это так:

df[df['is_desc']]

что:

   id  res  is_desc
0   1    3     True
1   2    1     True
2   1    2     True
5   1    1     True
0 голосов
/ 04 июля 2018

Выбор и сортировка ваших данных довольно проста и объективна. Однако решение о том, снижаются или нет данные пациента, может быть субъективным, поэтому лучше заранее определиться с критериями, чтобы увидеть, снижаются ли его данные.

Для сортировки и выбора:

import pandas as pd

data = [['pat_1', 10, 1],
        ['pat_1', 9, 2],
        ['pat_2', 11, 2],
        ['pat_1', 4, 5],
        ['pat_1', 2, 6],
        ['pat_2', 10, 1],
        ['pat_1', 7, 3],
        ['pat_1', 5, 4],        
        ['pat_2', 20, 3]]

df = pd.DataFrame(data).rename(columns={0:'Patient', 1:'Result', 2:'Day'})
print df

df_pat1 = df[df['Patient']=='pat_1']
print df_pat1

df_pat1_sorted = df_pat1.sort_values(['Day']).reset_index(drop=True)
print df_pat1_sorted

возвращается:

df:

  Patient  Result  Day
0   pat_1      10    1
1   pat_1       9    2
2   pat_2      11    2
3   pat_1       4    5
4   pat_1       2    6
5   pat_2      10    1
6   pat_1       7    3
7   pat_1       5    4
8   pat_2      20    3

df_pat1

  Patient  Result  Day
0   pat_1      10    1
1   pat_1       9    2
3   pat_1       4    5
4   pat_1       2    6
6   pat_1       7    3
7   pat_1       5    4

df_pat1_sorted

  Patient  Result  Day
0   pat_1      10    1
1   pat_1       9    2
2   pat_1       7    3
3   pat_1       5    4
4   pat_1       4    5
5   pat_1       2    6

Для целей этого ответа я скажу, что если первое значение нового DataFrame больше последнего, то их значения уменьшаются:

if df_pat1_sorted['Result'].values[0] > df_pat1_sorted['Result'].values[-1]:
print "Patient 1's values are declining"

Возвращает:

Patient 1's values are declining

Есть лучший способ, если у вас есть много уникальных идентификаторов (как, я уверен, у вас) для итераций по вашим пациентам. Я приведу пример с использованием целых чисел, однако вам может потребоваться использовать регулярное выражение, если ваши идентификаторы пациентов содержат символы.

import pandas as pd
import numpy as np

min_ID = 1003
max_ID = 1005
patients = np.random.randint(min_ID, max_ID, size=10)
df = pd.DataFrame(patients).rename(columns={0:'Patients'})
print df

s = pd.Series(df['Patients']).unique()
print s

for i in range(len(s)):
    print df[df['Patients']==s[i]]

возвращается:

   Patients
0      1004
1      1004
2      1004
3      1003
4      1003
5      1003
6      1003
7      1004
8      1003
9      1003

[1004 1003] # s (the unique values in the df['Patients'])

   Patients
3      1003
4      1003
5      1003
6      1003
8      1003
9      1003

   Patients
0      1004
1      1004
2      1004
7      1004

Надеюсь, это помогло!

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