Альтернатива для цикла по всем строкам pandas

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

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

 d = {
     'jobid': [100, 101,103,104,100,100,101], 
     'memberid': [1,2,3,3,3,2,1],
     'cluster':['bronze','silver','gold','gold','gold','silver','silver']
    }
df = pd.DataFrame(data=d)
df
jobid   memberid    cluster
0   100 1   bronze
1   101 2   silver
2   103 3   gold
3   104 3   gold
4   100 3   gold
5   100 2   silver
6   101 1   silver
  

Я нашел процент каждого кластера для каждого задания со следующим кодом:

 for i in df['jobid']:
    perc_bronze=round((df.loc[(df['jobid']==i) amp; (df['cluster']=='bronze')].count()[0]/df.loc[(df['jobid']==i)].count()[0])*100,2)
    df.loc[df['jobid']==i,'BronzeCluster']=perc_bronze
    perc_silver=round((df.loc[(df['jobid']==i) amp; (df['cluster']=='silver')].count()[0]/df.loc[(df['jobid']==i)].count()[0])*100,2)
    df.loc[df['jobid']==i,'SilverCluster']=perc_silver
    perc_gold=round((df.loc[(df['jobid']==i) amp; (df['cluster']=='gold')].count()[0]/df.loc[(df['jobid']==i)].count()[0])*100,2)
    df.loc[df['jobid']==i,'GoldCluster']=perc_gold
  

вывод:

     jobid   memberid    cluster BronzeCluster   SilverCluster   GoldCluster
0   100 1   bronze  33.33   33.33   33.33
1   101 2   silver  0.00    100.00  0.00
2   103 3   gold    0.00    0.00    100.00
3   104 3   gold    0.00    0.00    100.00
4   100 3   gold    33.33   33.33   33.33
5   100 2   silver  33.33   33.33   33.33
6   101 1   silver  0.00    100.00  0.00
  

Конечный результат верен, но проблема в том, что для запуска большого набора данных требуется много времени. Есть ли другой способ получить этот результат с меньшими вычислительными затратами?

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

1. Посмотрите pd.DataFrame.apply и pd.DataFrame.groupby .

2. Спасибо, Якуб. Я не знаю, как использовать apply, когда у меня есть условия для каждого кластера и задания

Ответ №1:

Вы можете использовать df.groupby и использовать GroupBy.value_counts divided by GroupBy.count , теперь использовать df.unstack , теперь объединить их df.merge с помощью параметра how, установленного на left .

 g = df.groupby('jobid')['cluster']
d = (g.value_counts().div(g.count())
       .mul(100).unstack(fill_value=0)
       .add_prefix('Cluster')
    )
df.merge(d, how='left', left_on='jobid', right_index=True)

   jobid  memberid cluster  Clusterbronze  Clustergold  Clustersilver
0    100         1  bronze      33.333333    33.333333      33.333333
1    101         2  silver       0.000000     0.000000     100.000000
2    103         3    gold       0.000000   100.000000       0.000000
3    104         3    gold       0.000000   100.000000       0.000000
4    100         3    gold      33.333333    33.333333      33.333333
5    100         2  silver      33.333333    33.333333      33.333333
6    101         1  silver       0.000000     0.000000     100.000000
  

Ответ №2:

Этот код:

 unstacked_df = df.groupby(['jobid', 'cluster']).count().unstack()
frequency_df = ((unstacked_df / unstacked_df.sum())*100).fillna(0)
print(frequency_df)
  

Выводит:

         memberid                      
cluster   bronze       gold     silver
jobid                                 
100        100.0  33.333333  33.333333
101          0.0   0.000000  66.666667
103          0.0  33.333333   0.000000
104          0.0  33.333333   0.000000
  

Это ожидаемое поведение?

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

1. Спасибо, но нет. сумма каждой строки для отдельных кластеров должна стать равной единице.

2. Что насчет этого: frequency_df = ((unstacked_df.div(unstacked_df.sum(axis=1), axis=0))*100).fillna(0)