Pandas дублированный индекс после отбрасывания - PullRequest
0 голосов
/ 01 августа 2020

Я получаю: «ValueError: индекс содержит повторяющиеся записи, не может быть изменен»

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


df.shape

>> (6820, 26) 

df.duplicated()

>> 0       False
>> 1       False
>> 2       False
>>        ...  
>> 6818    False
>> 6819    False
>> Length: 6820, dtype: bool

Теперь я хочу выяснить, какие строки дублируются.

df[df.duplicated(keep=False)]

>> 0 rows × 26 columns

Просто чтобы убедиться, что я ' m отбрасываю все дубликаты и оставляю только первый:

df = df.drop_duplicates(keep='first')

И вот тогда я получаю ValueError:

df2 = df.melt('Release')\
        .assign(variable = lambda x: x.variable.map({'Created Date':1,'Finished Date':-1}))\
        .pivot('value','Release','variable').fillna(0)\
        .rename(columns = lambda c: f'{c} netmov' )


---> 33         .pivot('value','Release','variable').fillna(0)\
ValueError: Index contains duplicate entries, cannot reshape

Из дальнейших исследований кажется, что дублируются не строки, а индекс. Я попытался сбросить индекс с помощью df.reset_index (), но он выдает ту же ошибку ValueError.

EDIT:

Я могу предоставить фиктивные данные, которые должны реплицировать фрейм, над которым я работаю с (всего на пару столбцов меньше, которые не нужны)

df = pd.DataFrame({'name': ["Peter", "Anna", "Anna", "Peter", "Simon", "Johan", "Nils", "Oskar", "Peter"]
                  , 'Deposits': ["2019-03-07", "2019-03-08", "2019-03-12", "2019-03-12", "2019-03-14", "2019-03-07", "2019-03-08", "2016-03-07", "2019-03-07"]
                  , 'Withdrawals': ["2019-03-11", "2019-03-19", "2019-05-22", "2019-10-31", "2019-04-05", "2019-03-11", "NaN", "2017-03-06", "2019-03-11"]})

df.duplicated()

0    False
1    False
2    False
.....
7    False
8     True
dtype: bool

df = df.drop_duplicates(keep='first')
df2 = df.melt('name')\
        .assign(variable = lambda x: x.variable.map({'Deposits':1,'Withdrawals':-1}))\
        .pivot('value','name','variable').fillna(0)\
        .rename(columns = lambda c: f'{c} netmov' )

df2 = pd.concat([df2,df2.cumsum().rename(columns = lambda c: c.split()[0] + ' balance')], axis = 1)\
        .sort_index(axis=1)


print(df2.head())

name        Anna balance  Anna netmov  Johan balance  Johan netmov  \
value                                                                
2016-03-07           0.0          0.0            0.0           0.0   
2017-03-06           0.0          0.0            0.0           0.0   
2019-03-07           0.0          0.0            1.0           1.0   
2019-03-08           1.0          1.0            1.0           0.0   
2019-03-11           1.0          0.0            0.0          -1.0

Это будет работать плавно, даже если в DataFrame есть дубликаты.

Предпочтительно, я не хочу отбрасывать дубликаты тоже, потому что «Анна» могла сделать 4 депозита и 4 снятия средств в течение дня, поэтому я хочу посчитать их все.

Фрейм данных, с которыми я работаю:


df = df.drop_duplicates().reset_index(drop=True)
df = df.drop(['id'], axis=1)
df

Output:

        name    Deposits     Withdrawals
0       Anna    2020-07-31   NaN
1       Peter   2020-07-30   NaN
2       Simon   2020-07-30   NaN
3       Simon   2020-07-29   NaN
4       Simon   2020-07-29   NaN
... ... ... ...
6154    Peter   2014-01-22  2014-02-03
6155    Peter   2014-01-22  2014-01-29
6156    Peter   2014-01-22  2014-01-24
6157    Peter   2014-01-21  2014-01-29
6158    Peter   2014-01-15  2014-02-03
6159 rows × 3 columns

Обновление: Приветствуем сообщество за помощь в решении этой проблемы.

Это решило проблему:

df.Deposits = pd.to_datetime(df.Deposits)
df.Withdrawals = pd.to_datetime(df.Withdrawals)

df2 = (
    df.melt('name') 
    .assign(variable = lambda x: x.variable.map({'Deposits':1,'Withdrawals':-1}))
    .dropna(subset=['value']) # you need this for cases like Nils's Withdrawal
    )
df2 = df2.groupby(['value', 'name']).sum().unstack(fill_value=0).droplevel(0, axis=1)


df2 = (
    pd.concat([df2, df2.cumsum()], keys=['netmov', 'balance'], axis=1)
     notice how concat has the functionality you want for naming columns
     and is a better idea to have netmov/balance in a separate level
     in case you want to groupby or .loc later on
    .reorder_levels([1, 0], axis=1).sort_index(axis=1)
    )

Тем не менее, наткнитесь на следующую проблему, не связанную с этим. При преобразовании этого DataFrame в json, он по какой-то причине преобразует даты в другой формат.

data = df2.to_json()
print(data)

{
    "Peter":
    {
        "1389744000000": 0,
        "1390262400000": 0,
        "1390348800000": 0,
        "1390521600000": 0,
    .....
    .....
    }
}

Это всегда что-то другое, хех ... приветствую помощь, хотя я почти дотрагиваюсь до цели -стр.

1 Ответ

2 голосов
/ 01 августа 2020

Проблема, по-видимому, возникает, когда имя имеет несколько движений в одни и те же даты депозита / снятия (отсюда и дублирование). Метод Dataframe .pivot не может обрабатывать повторяющиеся индексы, он просто не предназначен для этого. В целях вашего анализа .pivot_table подойдет, главное отличие состоит в том, что здесь можно применить функцию агрегирования для обработки повторяющихся индексов (в данном случае - суммы). https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pivot_table.html

Я лично склонен использовать .groupby с любыми проблемами такого рода, потому что он предлагает функциональность группировки не только по любой комбинации столбцов в df, но также может включать экзогенные серии, вычисления, индекс или уровни индекса себя или другого, маски и т. д. c.

Итак, мой код для этого будет:

df.Deposits = pd.to_datetime(df.Deposits)
df.Withdrawals = pd.to_datetime(df.Withdrawals) # this parsing probably happens in read_csv
df2 = (
    df.melt('name') 
    .assign(variable = lambda x: x.variable.map({'Deposits':1, 'Withdrawals':-1}))
    # use lambda if you must
    # replace on 'variable' after creating df2 would also work
    # and is probably faster for larger dfs
    .dropna(subset=['value']) # you need this for cases like Nils's Withdrawal
    )
df2 = df2.groupby(['value', 'name']).sum().unstack(fill_value=0).droplevel(0, axis=1)
df2 = (
    pd.concat([df2, df2.cumsum()], keys=['netmov', 'balance'], axis=1)
    # notice how concat has the functionality you want for naming columns
    # and is a better idea to have netmov/balance in a separate level
    # in case you want to groupby or .loc later on
    .reorder_levels([1, 0], axis=1).sort_index(axis=1)
    )

Вывод

name          Anna          Johan           Nils  ...  Oskar   Peter          Simon
           balance netmov balance netmov balance  ... netmov balance netmov balance netmov
value                                             ...
2016-03-07       0      0       0      0       0  ...      1       0      0       0      0
2017-03-06       0      0       0      0       0  ...     -1       0      0       0      0
2019-03-07       0      0       1      1       0  ...      0       2      2       0      0
2019-03-08       1      1       1      0       1  ...      0       2      0       0      0
2019-03-11       1      0       0     -1       1  ...      0       0     -2       0      0
2019-03-12       2      1       0      0       1  ...      0       1      1       0      0
2019-03-14       2      0       0      0       1  ...      0       1      0       1      1
2019-03-19       1     -1       0      0       1  ...      0       1      0       1      0
2019-04-05       1      0       0      0       1  ...      0       1      0       0     -1
2019-05-22       0     -1       0      0       1  ...      0       1      0       0      0
2019-10-31       0      0       0      0       1  ...      0       0     -1       0      0
...