как выполнить функцию транспонирования в фрейме данных pandas

#pandas #dataframe

Вопрос:

Я новичок в python, поэтому, пожалуйста, помогите мне.

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

df

         id    status     emailaddress  ownername   Key   
        a     pending    a.gmail.com   as          1
        a     pending     a.gmail.com   as         2
        a     submitted   a.gmail.com   as         3
        a     submitted   a.gmail.com   as         4
        b     pending    b.gmail.com    bs         1
        b     pending     b.gmail.com   bs         2
        b     pending     b.gmail.com   bs         3
        b     pending     b.gmail.com   bs         4
        b     pending    b.gmail.com    bs         1
        c     submitted   c.gmail.com   bs         1
        c     submitted   c.gmail.com   bs         3
        c     submitted   c.gmail.com   bs         4
 

Я хочу рассчитать количество ключевых столбцов на основе статуса для каждого идентификатора.

Также, если статус какой-либо один статус = ожидание, то общий статус этого пользователя будет ожидаться в сводной таблице.

Общий ключ = количество (Ключ) , где статус(в ожидании)

CompletedKey = количество (Ключ) , где статус(отправлен)

Ожидаемый df_summary

         id    status     emailaddress  ownername   TotalKey   CompletedKey  RemainingKey   
        a      pending    a.gmail.com   as         4           2              2
        b      pending    b.gmail.com   bs         5           0              4
        c      submitted  c.gmail.com   cs         3           3              0
        
 

Мой код

     dfResponse = df.groupby(['id','ownername','status','emailaddress'])['Key'].count().reset_index(name="TotalKey")     
 

Ответ №1:

Используется agg для вычисления счетчиков и слияния с исходным кадром данных. Чтобы сохранить правильный статус, создайте упорядоченный категориальный тип dtype, чтобы pending при сортировке значений статус отображался первым.

 >>> df.astype({'status': pd.CategoricalDtype(['pending', 'submitted'], ordered=True)}) 
      .merge(df.groupby('id')
               .agg(TotalKey=('Key', 'count'),
                    CompletedKey=('status', lambda x: sum(x == 'submitted')), 
                    RemainingKey=('status', lambda x: sum(x == 'pending'))),
             on='id') 
      .sort_values('status') 
      .drop_duplicates('id') 
      .drop(columns='Key')


  id     status emailaddress ownername  TotalKey  CompletedKey  RemainingKey
0  a    pending  a.gmail.com        as         4             2             2
4  b    pending  b.gmail.com        bs         5             0             5
9  c  submitted  c.gmail.com        bs         3             3             0
 

Ответ №2:

Используйте assign для создания логических столбцов для различных ключевых столбцов, которые вы хотите сделать, затем вы можете использовать sum после groupby . для столбца состояние сохраняйте where ожидание и используйте first после groupby , чтобы получить значение ожидание, когда по крайней мере один находится в группе, а затем fillna отсутствующее состояние по отправлено — потому что если нет, то только отправлено для этой группы.

 dfResponse = (
    df.assign(
        TotalKey=True, 
        CompletedKey=lambda x: x['status'].eq('submitted'),
        RemainingKey=lambda x: ~x['CompletedKey'], # assuming it is either submitted or pending
        status=lambda x: x['status'].where(x['RemainingKey'])) # keep only the pending values
      .groupby(['id','ownername','emailaddress']) # remove status from groupby
      .agg({'status':'first', # get pending if any pending in the group
            'TotalKey':sum, # sum because these columns are boolean
            'CompletedKey':sum,
            'RemainingKey':sum})
      .fillna({'status':'submitted'}) # fill missing status when no pending
      .reset_index()
)

print(dfResponse)
  id ownername emailaddress     status  TotalKey  CompletedKey  RemainingKey
0  a        as  a.gmail.com    pending         4             2             2
1  b        bs  b.gmail.com    pending         5             0             5
2  c        bs  c.gmail.com  submitted         3             3             0
 

Комментарии:

1. Вы можете упростить assign и agg за одну agg операцию, нет?

2. @Corralien да, я согласен, сначала я пошел этим путем, так как не видел требований к столбцу статуса, и использование assign было способом сделать groupby.sum это непосредственно для всех ключевых столбцов. После добавления столбца статуса он больше не является лучшим..

Ответ №3:

Вы можете объединить .groupby (для определения количества каждой ожидающей/отправленной группы) .pivot_table (для изменения формы/суммирования фрейма данных):

 x = df.groupby(["id", "status"], as_index=False).agg(
    {
        "status": "first",
        "emailaddress": "first",
        "ownername": "first",
        "Key": "count",
    }
)
x = x.pivot_table(
    index=["id", "emailaddress", "ownername", "status"],
    columns="status",
    aggfunc="sum",
)
x.columns = x.columns.map("_".join)
x = (
    x.rename(
        columns={"Key_pending": "RemainingKey", "Key_submitted": "CompletedKey"}
    )
    .fillna(0)
    .astype(int)
)
x["TotalKey"] = x.sum(1)
print(x.reset_index().sort_values(["id", "status"]).drop_duplicates("id"))
 

С принтами:

   id emailaddress ownername     status  RemainingKey  CompletedKey  TotalKey
0  a  a.gmail.com        as    pending             2             0         2
2  b  b.gmail.com        bs    pending             5             0         5
3  c  c.gmail.com        bs  submitted             0             3         3
 

Комментарии:

1. @Ben. Готово. 🙂