У меня есть такой фрейм данных:

df = pd.DataFrame([['A', 3, 'fox'], ['A', 3, 'cat'], ['A', 3, 'dog'],
                   ['B', 2, 'rabbit'], ['B', 2, 'dog'], ['B', 2, 'eel'],
                   ['C', 6, 'fox'], ['C', 6, 'elephant']],
                  columns=['group', 'val', 'animal'])
df

Выход:

    group   val animal
0   A       3   fox
1   A       3   cat
2   A       3   dog
3   B       2   rabbit
4   B       2   dog
5   B       2   eel
6   C       6   fox
7   C       6   elephant

Для данной группы val всегда одно и то же (поэтому всегда 3 для A, 2 для B, 6 для C).

Как я могу создать фрейм данных со всеми комбинациями элементов group и animal? Также должен быть перенесен val, и должен быть столбец, указывающий, присутствовала ли эта строка в исходных данных или добавлена в перестановках.

Желаемый результат:

df = pd.DataFrame([['A', 3, 'fox', 1], ['A', 3, 'cat', 1], ['A', 3, 'dog', 1], ['A', 3, 'rabbit', 0], ['A', 3, 'eel', 0], ['A', 3, 'elephant', 0],
                   ['B', 2, 'rabbit', 1], ['B', 2, 'dog', 1], ['B', 2, 'eel', 1], ['B', 2, 'fox', 0], ['B', 2, 'cat', 0], ['B', 2, 'elephant', 0],
                   ['C', 6, 'fox', 1], ['C', 6, 'elephant', 1], ['C', 6, 'cat', 0], ['C', 6, 'dog', 0], ['C', 6, 'rabbit', 0], ['C', 6, 'eel', 0]], 
                  columns=['group', 'val', 'animal', 'occurred'])
df

Выход:

    group   val animal  occurred
0   A       3   fox         1
1   A       3   cat         1
2   A       3   dog         1
3   A       3   rabbit      0
4   A       3   eel         0
5   A       3   elephant    0
6   B       2   rabbit      1
7   B       2   dog         1
8   B       2   eel         1
9   B       2   fox         0
10  B       2   cat         0
11  B       2   elephant    0
12  C       6   fox         1
13  C       6   elephant    1
14  C       6   cat         0
15  C       6   dog         0
16  C       6   rabbit      0
17  C       6   eel         0

Как я могу это сделать?

Изменить: было несколько ответов, которые работают. Я отдаю должное тому, что можно сделать, чтобы учесть возможность соединения нескольких столбцов с group (например, не только 'val', но ['val1','val2']) элегантным способом.

3
Mobeus Zoom 18 Авг 2020 в 04:01

5 ответов

Лучший ответ

Обновлено для нескольких значений:

df = pd.DataFrame([['A', 3, 4, 'fox'], ['A', 3, 4, 'cat'], ['A', 3, 4,'dog'],
                   ['B', 2, 3, 'rabbit'], ['B', 2, 3, 'dog'], ['B', 2, 3,'eel'],
                   ['C', 6, 7, 'fox'], ['C', 6, 7, 'elephant']],
                  columns=['group', 'val1', 'val2', 'animal'])


dfi = df.set_index(['group', 'animal']).assign(occurred=1)
indx = pd.MultiIndex.from_product(dfi.index.levels)
dfi = dfi.reindex(indx, fill_value=0)
dfi[['val1', 'val2']]  = dfi.groupby(level=0)[['val1','val2']].transform('max')
print(dfi.reset_index().sort_values(['group', 'occurred'], ascending=[True, False]))

Выход:

   group    animal  val1  val2  occurred
0      A       cat     3     4         1
1      A       dog     3     4         1
4      A       fox     3     4         1
2      A       eel     3     4         0
3      A  elephant     3     4         0
5      A    rabbit     3     4         0
7      B       dog     2     3         1
8      B       eel     2     3         1
11     B    rabbit     2     3         1
6      B       cat     2     3         0
9      B  elephant     2     3         0
10     B       fox     2     3         0
15     C  elephant     6     7         1
16     C       fox     6     7         1
12     C       cat     6     7         0
13     C       dog     6     7         0
14     C       eel     6     7         0
17     C    rabbit     6     7         0

IIUC, вы можете сделать это так: назначить 'Observed', используя set_index, создать мультииндекс, затем groupby для заполнения NaN.

dfi = df.set_index(['group', 'animal']).assign(occurred=1)
indx = pd.MultiIndex.from_product(dfi.index.levels)
dfi = dfi.reindex(indx, fill_value=0)
dfi['val']  = dfi.groupby(level=0)['val'].transform('max')
dfi.reset_index().sort_values(['group', 'occurred'], ascending=[True, False])

Выход:

   group    animal  val  occurred
0      A       fox    3         1
1      A       cat    3         1
2      A       dog    3         1
3      A    rabbit    3         0
4      A       eel    3         0
5      A  elephant    3         0
8      B       dog    2         1
9      B    rabbit    2         1
10     B       eel    2         1
6      B       fox    2         0
7      B       cat    2         0
11     B  elephant    2         0
12     C       fox    6         1
17     C  elephant    6         1
13     C       cat    6         0
14     C       dog    6         0
15     C    rabbit    6         0
16     C       eel    6         0
2
Scott Boston 18 Авг 2020 в 18:50

Вы хотите сделать сводную таблицу.

Это делается в Pandas с помощью pandas.pivot_table (data, values = None, index = None, columns = None, aggfunc = 'mean', fill_value = None, margins = False, dropna = True, margins_name = 'All', наблюдается = False) команда.

Как видите, pandas.pivot_table () принимает множество аргументов, но есть несколько основных. В вашем случае вы ищете pandas.pivot_table (df, columns = ["group", "animal"], aggfunc = custom).

Custom - это функция, которую вам нужно будет написать выше выполнения строки pivot_table (), чтобы получить описанную вами функциональность: «val должен быть перенесен, и должен быть столбец, указывающий, присутствовала ли эта строка в исходных данных или добавлена в перестановки ".

0
Naman Patel 18 Авг 2020 в 01:24

Пытаться:

df2 = pd.DataFrame(list(product(df.group.unique(), df.animal.unique())), columns=['group', 'animal'])
df2['val'] = df2['group'].map(df.set_index('group')['val'].to_dict())
df2.merge(df.drop('val', axis=1).assign(occurred=1), how='outer').fillna(0, downcast='infer')
1
Sandeep Kadapa 18 Авг 2020 в 01:54

Одно из решений - использовать {{X0} } с MultiIndex:

mux = pd.MultiIndex.from_product([df['group'].unique(), df['animal'].unique()], names=('group','animal'))
df = df.set_index(['group','animal']).reindex(mux).reset_index()
df['occurred'] = df['val'].notnull().astype(int)
df['val'] = df.groupby('group')['val'].transform('first')

Результат:

   group    animal  val  occurred
0      A       fox  3.0         1
1      A       cat  3.0         1
2      A       dog  3.0         1
3      A    rabbit  3.0         0
4      A       eel  3.0         0
5      A  elephant  3.0         0
6      B       fox  2.0         0
7      B       cat  2.0         0
8      B       dog  2.0         1
9      B    rabbit  2.0         1
10     B       eel  2.0         1
11     B  elephant  2.0         0
12     C       fox  6.0         1
13     C       cat  6.0         0
14     C       dog  6.0         0
15     C    rabbit  6.0         0
16     C       eel  6.0         0
17     C  elephant  6.0         1

Расширение:

Для обработки нескольких столбцов val используйте список имен столбцов, а не только 'val'. Незначительно изменены только два последних ряда.

val_cols = ['val1', 'val2']

mux = pd.MultiIndex.from_product([df['group'].unique(), df['animal'].unique()], names=('group','animal'))
df = df.set_index(['group','animal']).reindex(mux).reset_index()
df['occurred'] = df[val_cols[0]].notnull().astype(int)
df[val_cols ] = df.groupby('group')[val_cols].transform('first')
3
Shaido - Reinstate Monica 19 Авг 2020 в 01:12

Обновите, теперь он может обрабатывать один или несколько val.

Я вставляю одни данные (['A', 3, 'fox']), и они все еще работают.

import pandas as pd

df_single = pd.DataFrame([['A', 3, 'fox'], ['A', 3, 'cat'], ['A', 3, 'dog'],
                          ['A', 3, 'fox'],  # <-- new data
                          ['B', 2, 'rabbit'], ['B', 2, 'dog'], ['B', 2, 'eel'],
                          ['C', 6, 'fox'], ['C', 6, 'elephant'],
                          ],
                         columns=['group', 'val', 'animal'])

df_multiple = pd.DataFrame([['A', 3, 4, 'fox'], ['A', 3, 4, 'cat'], ['A', 3, 4, 'dog'],
                            ['A', 3, 4, 'fox'],  # <-- new data  #  Others solution will occur ValueError("cannot handle a non-unique multi-index!")
                            ['B', 2, 3, 'rabbit'], ['B', 2, 3, 'dog'], ['B', 2, 3, 'eel'],
                            ['C', 6, 7, 'fox'], ['C', 6, 7, 'elephant'],
                            ],
                           columns=['group', 'val', 'val2', 'animal'])


def solution(df, val_col_name_list: list):
    animal_set = df.animal.unique()
    data_set = set(zip(*[df[col] for col in (['group'] + val_col_name_list)]))

    df_occurred = df.groupby(['group', 'animal']).aggregate(
        occurred=pd.NamedAgg(column=val_col_name_list[0], aggfunc='count')
    )
    df_occurred.reset_index(inplace=True)

    df_output = pd.DataFrame(columns=df.columns.to_list() + ['occurred'])

    for cur_animal in animal_set:
        for cur_group, *val in data_set:
            s: pd.Series = df_occurred[(df_occurred.group == cur_group) & (df_occurred.animal == cur_animal)]
            occurred: int = s.occurred.values[0] if len(s) else 0
            df_output.loc[len(df_output)] = [cur_group] + val + [cur_animal, occurred]  # insert data to last row
    print(df_output.sort_values(['group', 'occurred'], ascending=[True, False]).reset_index(drop=True))


if __name__ == '__main__':
    solution(df_single, ['val'])
    solution(df_multiple, ['val', 'val2'])

   group val    animal occurred
0      A   3       fox        2
1      A   3       cat        1
2      A   3       dog        1
3      A   3    rabbit        0
4      A   3       eel        0
5      A   3  elephant        0
6      B   2       dog        1
7      B   2    rabbit        1
8      B   2       eel        1
9      B   2       fox        0
10     B   2       cat        0
11     B   2  elephant        0
12     C   6       fox        1
13     C   6  elephant        1
14     C   6       cat        0
15     C   6       dog        0
16     C   6    rabbit        0
17     C   6       eel        0

   group val val2    animal occurred
0      A   3    4       fox        2
1      A   3    4       cat        1
2      A   3    4       dog        1
3      A   3    4    rabbit        0
4      A   3    4       eel        0
5      A   3    4  elephant        0
6      B   2    3       dog        1
7      B   2    3    rabbit        1
8      B   2    3       eel        1
9      B   2    3       fox        0
10     B   2    3       cat        0
11     B   2    3  elephant        0
12     C   6    7       fox        1
13     C   6    7  elephant        1
14     C   6    7       cat        0
15     C   6    7       dog        0
16     C   6    7    rabbit        0
17     C   6    7       eel        0
1
Carson 19 Авг 2020 в 02:14