Отфильтруйте данные по динамическим значениям столбца, подобным ARRAY c. - PullRequest
2 голосов
/ 09 марта 2020

У меня есть образцы данных, которые можно загрузить с здесь . Что я хочу сделать, чтобы отфильтровать эти данные на основе element_counts, cluster_choice_prob_k_fold, benchmark_probabilities столбцов для каждого человека.

Это столбец logi c: element_counts имеет массивоподобную структуру [3, 2, 0 , 0, 0] (в данном случае). Он показывает, сколько элементов я должен выбрать из верхних значений benchmark_probabilities из каждого верхнего 5 cluster_choice_prob_k_fold значений. В этом примере этот массив сообщает, что:

  1. Из самой большой группы cluster_choice_prob_k_fold выберите 3 значения на основе их benchmark_probabilities
  2. Из второй по величине группы cluster_choice_prob_k_fold выберите 2 значения на основе их benchmark_probabilities
  3. Из третьей по величине группы cluster_choice_prob_k_fold выберите 0 значений на основе их benchmark_probabilities
  4. Аналогично 3
  5. Аналогично 4.

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

individual  cluster_choice_prob_k_fold  benchmark_probabilities element_counts
9710535 0.512776    0.163837    [3, 2, 0, 0, 0]
9710535 0.512776    0.0986      [3, 2, 0, 0, 0]
9710535 0.512776    0.085191    [3, 2, 0, 0, 0]
9710535 0.294674    0.050787    [3, 2, 0, 0, 0]
9710535 0.294674    0.037609    [3, 2, 0, 0, 0]

Для других значений individual у меня есть массив element_counts, который выглядит по-разному, но всегда с 5 элементами и всегда значения элементов добавляются до 5. Как я могу сделать это с Pandas? Сложность состоит в том, чтобы написать функцию, которая может учитывать различные массивы, и использовать ее с groupby

Ответы [ 2 ]

1 голос
/ 14 марта 2020

Идея решения - создать вспомогательный столбец для количества отсортированных групп и фильтрации.

Решение, работающее с одинаковыми значениями element_counts для каждой группы по столбцу individual, также всегда есть 5 значений в списках .

Сначала, потому что образцы данных находятся в одном столбце, некоторая предварительная обработка:

import ast

df = pd.read_excel('sample.xlsx')
c = df.columns[0].split(',')
df = df.iloc[:, 0].str.split(',', expand=True, n=3).applymap(lambda x: x.strip('"'))
df.columns = c
df['individual'] = df['individual'].astype(int)
c = ['cluster_choice_prob_k_fold','benchmark_probabilities']
df[c] = df[c].astype(float)
print (df.head())
   individual  cluster_choice_prob_k_fold  benchmark_probabilities  \
0     9710535                    0.512776                 0.163837   
1     9710535                    0.512776                 0.098600   
2     9710535                    0.512776                 0.085191   
3     9710535                    0.512776                 0.067577   
4     9710535                    0.512776                 0.065420   

    element_counts  
0  [3, 2, 0, 0, 0]  
1  [3, 2, 0, 0, 0]  
2  [3, 2, 0, 0, 0]  
3  [3, 2, 0, 0, 0]  
4  [3, 2, 0, 0, 0]  

Затем сортировка по обоим столбцам по DataFrame.sort_values и преобразование строки repr списков в списки целых чисел по ast.literal_eval:

df = df.sort_values(['cluster_choice_prob_k_fold','benchmark_probabilities'], ascending=False)
df['element_counts'] = df['element_counts'].apply(ast.literal_eval)

Затем создайте счетчик групп по GroupBy.transform и factorize:

df['g'] = df.groupby('individual', sort=False)['cluster_choice_prob_k_fold'].transform(lambda x: pd.factorize(x)[0]).astype(int)

Создание нового DataFrame с удалением дубликатов списков с *1029*DataFrame.drop_duplicates, DataFrame.explode в новый столбец и создание столбца для групп с numpy.tile:

df1 = (df.drop_duplicates('individual')
         .explode('element_counts')[['individual','element_counts']]
         .assign(g = np.tile(np.arange(5), df['individual'].nunique())))
print (df1)
   individual element_counts  g
0     9710535              3  0
0     9710535              2  1
0     9710535              0  2
0     9710535              0  3
0     9710535              0  4

Так что возможно использовать DataFrame.merge с левым соединением к оригиналу и удалить несоответствующие группы с пропущенными значениями на DataFrame.dropna:

df = (df.merge(df1, how='left', on=['individual','g'], suffixes=('','_'))
        .dropna(subset=['element_counts_']))
print (df.head(10))
   individual  cluster_choice_prob_k_fold  benchmark_probabilities  \
0     9710535                    0.512776                 0.163837   
1     9710535                    0.512776                 0.098600   
2     9710535                    0.512776                 0.085191   
3     9710535                    0.512776                 0.067577   
4     9710535                    0.512776                 0.065420   
5     9710535                    0.512776                 0.054764   
6     9710535                    0.512776                 0.048068   
7     9710535                    0.512776                 0.018973   
8     9710535                    0.294674                 0.050787   
9     9710535                    0.294674                 0.037609   

    element_counts  g element_counts_  
0  [3, 2, 0, 0, 0]  0               3  
1  [3, 2, 0, 0, 0]  0               3  
2  [3, 2, 0, 0, 0]  0               3  
3  [3, 2, 0, 0, 0]  0               3  
4  [3, 2, 0, 0, 0]  0               3  
5  [3, 2, 0, 0, 0]  0               3  
6  [3, 2, 0, 0, 0]  0               3  
7  [3, 2, 0, 0, 0]  0               3  
8  [3, 2, 0, 0, 0]  1               2  
9  [3, 2, 0, 0, 0]  1               2  

И последнее использование GroupBy.apply с DataFrame.head для фильтрации строк по группам по добавленному столбцу n, последний удалил оба вспомогательных столбца:

df = (df.groupby(['individual', 'cluster_choice_prob_k_fold'], 
                 group_keys=False, 
                 sort=False)
        .apply(lambda x: x.head(x['element_counts_'].iat[0]))
        .drop(['g','element_counts_'], axis=1))
print (df)
   individual  cluster_choice_prob_k_fold  benchmark_probabilities  \
0     9710535                    0.512776                 0.163837   
1     9710535                    0.512776                 0.098600   
2     9710535                    0.512776                 0.085191   
8     9710535                    0.294674                 0.050787   
9     9710535                    0.294674                 0.037609   

    element_counts  
0  [3, 2, 0, 0, 0]  
1  [3, 2, 0, 0, 0]  
2  [3, 2, 0, 0, 0]  
8  [3, 2, 0, 0, 0]  
9  [3, 2, 0, 0, 0]  
0 голосов
/ 12 марта 2020

Честно говоря, решение с pandas .DataFrame.groupby мне не приходит в голову. Тем не менее, я написал немного грубое решение, которое работает. Если вы хотите попробовать его, вот оно (предположим, что df будет вашим примером данных):

dfs = []

for element_count in df.element_counts.unique():
    element_count_list = element_count.strip('[').strip(']').split(', ')
    element_count_list_of_ints = [int(i) for i in element_count_list]
    top_probas = list(df.cluster_choice_prob_k_fold.unique())[:len(element_count_list_of_ints)]

    for n, prob in zip(element_count_list_of_ints, top_probas):
        df_part = df_element\
            .loc[(df.cluster_choice_prob_k_fold == prob) & (df.element_counts == element_count), :]\
            .sort_values(by='benchmark_probabilities', ascending=False)\
            .head(n)\
            .copy()
        dfs.append(df_part)

df_final = pd.concat(dfs, ignore_index=True)

Окончательный кадр данных выглядит следующим образом:

>>> print(df_final)
   individual  cluster_choice_prob_k_fold  benchmark_probabilities  \
0     9710535                    0.512776                 0.163837   
1     9710535                    0.512776                 0.098600   
2     9710535                    0.512776                 0.085191   
3     9710535                    0.294674                 0.050787   
4     9710535                    0.294674                 0.037609   

    element_counts  
0  [3, 2, 0, 0, 0]  
1  [3, 2, 0, 0, 0]  
2  [3, 2, 0, 0, 0]  
3  [3, 2, 0, 0, 0]  
4  [3, 2, 0, 0, 0]  
...