#pandas #dataframe #filtering #data-manipulation
Вопрос:
У меня есть фрейм данных, состоящий из медицинских данных, где столбцы [«Patient_ID», «Код», «Данные»], где «Код» просто представляет некоторое медицинское взаимодействие, которое пациент «Patient_ID» имел на «Дату». У любого пациента, как правило, будет более одного ряда, так как у них более одного взаимодействия. Я хочу применить к этим данным два типа фильтрации.
- Удалите всех пациентов, у которых меньше, чем у некоторых
min_len
взаимодействий - К каждому пациенту прикладывают наполовину перекрывающееся, скользящее окно продолжительности
T
дней. В каждом окне сохраняйте только первый из любых повторяющихся кодов, а затем перетасуйте коды в окне
Поэтому мне нужно изменить подмножества общего фрейма данных, но модификация включает в себя изменение размера подмножества. У меня оба они реализованы как часть более крупного конвейера, однако они являются существенным узким местом с точки зрения времени. Мне интересно, есть ли более эффективный способ добиться того же самого, так как я действительно просто собрал то, что сработало, и я не слишком хорошо знаком с эффективностью операций pandas. Вот как они у меня есть в настоящее время:
def Filter_by_length(df, min_len = 1):
print("Filtering short sequences...")
df = df.sort_values(axis = 0, by = ['ID', 'DATE']).copy(deep = True)
new_df = []
for sub_df in tqdm((df[df.ID == sub] for sub in df.ID.unique()), total = len(df.ID.unique()), miniters = 1):
if len(sub_df) >= min_len:
new_df.append(sub_df.copy(deep = True))
if len(new_df) != 0:
df = pd.concat(new_df, sort = False)
else:
df = pd.DataFrame({})
print("Done")
return df
def shuffle_col(df, col):
df[col] = np.random.permutation(df[col])
return df
def Filter_by_redundancy(df, T, min_len = 1):
print("Filtering redundant concepts and short sequences...")
df = df.sort_values(axis = 0, by = ['ID', 'DATE']).copy(deep = True)
new_df = []
for sub_df in tqdm((df[df.ID == sub] for sub in df.ID.unique()), total = len(df.ID.unique()), miniters = 1):
start_date = sub_df.DATE.min()
end_date = sub_df.DATE.max()
next_date = start_date dt.timedelta(days = T)
while start_date <= end_date:
sub_df = pd.concat([sub_df[sub_df.DATE < start_date],
shuffle_col(sub_df[(sub_df.DATE <= next_date) amp; (sub_df.DATE >= start_date)]
.drop_duplicates(subset = ['CODE']), "CODE"),
sub_df[sub_df.DATE > next_date]], sort = False )
start_date = dt.timedelta(days = int(T/2))
next_date = dt.timedelta(days = int(T/2))
if len(sub_df) >= min_len:
new_df.append(sub_df.copy(deep = True))
if len(new_df) != 0:
df = pd.concat(new_df, sort = False)
else:
df = pd.DataFrame({})
print("Done")
return df
Как вы можете видеть, во втором случае я фактически применяю оба фильтра, потому что важно иметь возможность применять оба вместе или один отдельно, но я заинтересован в любом повышении производительности, которое может быть сделано для одного или обоих.
Комментарии:
1. Получите длину группы
df.groupby('id').size()
, а затем просто удалите любой идентификатор с недостаточной длиной. Я не уверен, что делает ваш другой раздел в целом, но вы также можете заменить любое время, когда скажетеfor sub_df in [df[df['id'] == i] for i in df['id'].unique()]
, наfor id, sub_df in df.groupby('id')
2. Этот подход к длине намного быстрее, спасибо.
Ответ №1:
Для первой части, вместо того, чтобы считать в вашей группе таким образом, я бы использовал этот подход:
>>> d = pd.DataFrame({'id': [1, 2, 3, 4, 5], 'q': [np.random.randint(1, 15, size=np.random.randint(1, 5)) for _ in range(5)]}).explode('q')
id q
0 1 1
0 1 9
1 2 9
1 2 10
1 2 4
2 3 3
2 3 6
2 3 2
2 3 10
3 4 11
3 4 5
4 5 5
4 5 6
4 5 3
4 5 2
>>> sizes = d.groupby('id').size()
>>> d[d['id'].isin(sizes[sizes >= 3].index)] # index is list of IDs meeting criteria
id q
1 2 9
1 2 10
1 2 4
2 3 3
2 3 6
2 3 2
2 3 10
4 5 5
4 5 6
4 5 3
4 5 2
Я не совсем понимаю, почему вы хотите перетасовать свои коды в каком-то окне. Чтобы избежать проблемы X-Y, что вы на самом деле пытаетесь там сделать?
Комментарии:
1. Я попробую первый подход, спасибо. Идея перетасовки заключается в том, что в целом важен последовательный характер данных, но в течение некоторого временного интервала порядок является произвольным. В качестве простейшего примера, если пациенту назначают 3 препарата в один и тот же день, то заказ, если он не важен для этих 3 препаратов. Это тоже реализация работы из документа, а не то, что я решил сделать сам. Конечно, возможно, что перетасовка окажется бессмысленной.