Сводный кадр данных по столбцу (ID) дублирован - PullRequest
1 голос
/ 03 марта 2020

У меня есть DataFrame со столбцом с именем 'ID', в котором есть повторяющиеся наблюдения. Каждая строка 'ID' имеет один или несколько столбцов значений 'Article'. Я хочу транспонировать всю группу данных с помощью идентификатора, добавляя новые столбцы в той же строке уникального идентификатора. Порядок важен

Мой DataFrame:

ID  Article     Article_2
1   Banana      NaN
2   Apple       NaN
1   Apple       Coconut
3   Tomatoe     Coconut
1   Pineapple   Tropical
2   Banana      Coconut
4   Apple       Coconut
5   Apple       Coconut
3   Apple       Pineapple

Мой код (от @Erfan):

dfn = pd.melt(df1, id_vars='ID', value_vars=['Article', 'Article_2'])
dfn = dfn.pivot_table(index='ID', 
                      columns=dfn.groupby('ID')['value'].cumcount().add(1),
                      values='value',
                      aggfunc='first').add_prefix('Article_').rename_axis(None, axis='index')

Вывод:

        Article_1   Article_2   Article_3   Article_4   Article_5   Article_6
0001    Banana      Apple       Pineapple   NaN         Coconut     Tropical
0002    Apple       Banana      NaN         Coconut     NaN         NaN
0003    Tomatoe     Apple       Coconut     Pineapple   NaN         NaN
0004    Apple       Coconut     NaN         NaN         NaN         NaN
0005    Apple       Coconut     NaN         NaN         NaN         NaN

В В первой строке Article_4 - NaN, а Article_5 и 6 имеют значения. Это должны быть Article_4 Coconut, Article_5 Tropical и Article_6 NaN. Во втором случае Article_3 - это NaN, а Article_4 - допустимое значение. Это должен быть Article_3 действительный и остальные (4,5,6) NaNs

Необходимый результат:

        Article_1   Article_2   Article_3   Article_4   Article_5   Article_6
0001    Banana      Apple       Pineapple   Coconut     Tropical    NaN     
0002    Apple       Banana      Coconut     NaN         NaN         NaN
0003    Tomatoe     Apple       Coconut     Pineapple   NaN         NaN
0004    Apple       Coconut     NaN         NaN         NaN         NaN
0005    Apple       Coconut     NaN         NaN         NaN         NaN

1 Ответ

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

Добавить DataFrame.dropna после melt для удаления пропущенных строк по столбцу value:

dfn = pd.melt(df1, id_vars='ID', value_vars=['Article', 'Article_2']).dropna(subset=['value'])

dfn = dfn.pivot_table(index='ID', 
                      columns=dfn.groupby('ID')['value'].cumcount().add(1),
                      values='value',
                      aggfunc='first').add_prefix('Article_').rename_axis(None, axis='index')

print (dfn)
  Article_1 Article_2  Article_3  Article_4 Article_5
1    Banana     Apple  Pineapple    Coconut  Tropical
2     Apple    Banana    Coconut        NaN       NaN
3   Tomatoe     Apple    Coconut  Pineapple       NaN
4     Apple   Coconut        NaN        NaN       NaN
5     Apple   Coconut        NaN        NaN       NaN

При необходимости во всех столбцах используется немного измененная функция justify:

dfn = pd.melt(df1, id_vars='ID', value_vars=['Article', 'Article_2'])

dfn = dfn.pivot_table(index='ID', 
                      columns=dfn.groupby('ID')['value'].cumcount().add(1),
                      values='value',
                      aggfunc='first').add_prefix('Article_').rename_axis(None, axis='index')

#https://stackoverflow.com/a/44559180/2901002
def justify(a, invalid_val=0, axis=1, side='left'):    
    """
    Justifies a 2D array

    Parameters
    ----------
    A : ndarray
        Input array to be justified
    axis : int
        Axis along which justification is to be made
    side : str
        Direction of justification. It could be 'left', 'right', 'up', 'down'
        It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.

    """

    if invalid_val is np.nan:
        mask = pd.notna(a)
    else:
        mask = a!=invalid_val
    justified_mask = np.sort(mask,axis=axis)
    if (side=='up') | (side=='left'):
        justified_mask = np.flip(justified_mask,axis=axis)
    out = np.full(a.shape, invalid_val, dtype=object) 
    if axis==1:
        out[justified_mask] = a[mask]
    else:
        out.T[justified_mask.T] = a.T[mask.T]
    return out

dfn = pd.DataFrame(justify(dfn.values, invalid_val=np.nan, axis=1, side='left'),
                   index=dfn.index, columns=dfn.columns)
print (dfn)
  Article_1 Article_2  Article_3  Article_4 Article_5 Article_6
1    Banana     Apple  Pineapple    Coconut  Tropical       NaN
2     Apple    Banana    Coconut        NaN       NaN       NaN
3   Tomatoe     Apple    Coconut  Pineapple       NaN       NaN
4     Apple   Coconut        NaN        NaN       NaN       NaN
5     Apple   Coconut        NaN        NaN       NaN       NaN
...