Обновите фрейм данных на основе значений другого - PullRequest
0 голосов
/ 06 мая 2019

У меня есть датафрейм, который состоит из идентификаторов и дат.У одного идентификатора может быть несколько дат - идентификаторы отсортированы по датам для каждого идентификатора.

AccidentDates

Мой второй кадр данных состоит из идентификаторов, даты начала,Дата завершения, логический столбец Авария (указывает на возникновение аварии) и столбец Время до события.Последние два столбца изначально установлены на 0. Идентификаторы отсортированы, а также временные интервалы для каждого идентификатора еще раз.

PatientLog

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

Если было, найдите, в каком интервале это произошло, обновите столбец Авария до 1 и Time = df1.Date - df2.Start.Если нет, установите Accident = 0 и Time = df2.Finish - df2.Start для этой записи пациента.

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

# Temporary lists
df1list = []
df2list = []

# Change format from dataframe to list
for row in df1.itertuples(index=True, name='Pandas'):

    # Get Patient ID and the date of the recorded accident
    df1list.append([getattr(row, "Patient"), getattr(row, "regdatum")])


# Change format from dataframe to list
for row in df2.itertuples(index=True, name='Pandas'):

    # Get Patient ID, info, occurrence of accident and time to event
    df2list.append([getattr(row, "Patient"), getattr(row, "Start"), getattr(row, "Finish"), getattr(row, "Gender"),
                   getattr(row, "Age"), getattr(row, "Accident"), getattr(row, "Time")])


#For each interval of each patient
for i in range(0, len(df2list)):

    #For each recorded accident of each patient
    for j in range(0, len(df1list)):

        #If there's a match in both lists
        if df2list[i][0] == df1list[j][0]:

            #If the recorded date is in between the time interval
            if (df1list[j][1] >= datetime.strptime(df2list[i][1], '%Y-%m-%d')) & (df1list[j][1] <= datetime.strptime(df2list[i][2], '%Y-%m-%d')):

                #Change the accident column to 1 and calculate the time to event
                #The extra if is to verify that this is the recorded accident is the first one to have happened within the time interval (if there are multiple, we only keep the first one)    
                if df2list[i][6] == 0 :
                    df2list[i][6] = 1
                    df2list[i][7] = df1list[j][1] - datetime.strptime(df2list[i][1], '%Y-%m-%d')

#Back to dfs
labels = ['Patient', 'Start', 'Finish', 'Gender', 'Age', 'Accident', 'Time']
df = pd.DataFrame.from_records(df2list, columns=labels)
```

1 Ответ

0 голосов
/ 06 мая 2019

Вот как бы я это сделал.

# Define a pair of functions that return the list of unique start and end dates for a given patient
def start_dates(patient):
    try:
        return df2.loc[df2['Patient'] == patient]['Start'].unique()
    except:
        return np.datetime64("NaT")

def finish_dates(patient):
    try:
        return df2.loc[df2['Patient'] == patient]['Finish'].unique()
    except:
        return np.datetime64("NaT")

# Add and fill 'Start' and 'Finish' columns to df1
df1['Start'] = list(zip(df1['Patient'], df1['Accident Date']))
df1['Start'] = df1['Start'].apply(lambda x: max([d for d in start_dates(x[0]) if d <= np.datetime64(x[1])]))
df1['Finish'] = list(zip(df1['Patient'], df1['Accident Date']))
df1['Finish'] = df1['Finish'].apply(lambda x: min([d for d in finish_dates(x[0]) if d >= np.datetime64(x[1])]))

# Merge the two DataFrames
df2 = df2.merge(df1, how='outer')

# Fill the 'Accident' column appropriately, and convert to int
df2['Accident'] = ~pd.isna(df2.iloc[:,5])
df2 = df2.astype({'Accident': int})

# Fill NaT fields in 'Accident Date' with 'Finish'
df2 = df2.fillna({'Accident Date': df2['Finish']})

# Fill 'Time' appropriately
df2['Time'] = df2['Accident Date'] - df2['Start']

# Drop the 'Accident Date' column
df2 = df2.drop(columns=['Accident Date'])

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

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