Идея решения - создать вспомогательный столбец для количества отсортированных групп и фильтрации.
Решение, работающее с одинаковыми значениями 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]