2
votes

Somme cumulative d'une colonne pandas jusqu'à ce qu'une valeur maximale soit atteinte et moyenne des lignes adjacentes

Je suis un étudiant en biologie qui est assez nouveau en python et j'espérais que quelqu'un pourrait être en mesure de m'aider avec un problème que je n'ai pas encore résolu

Avec un peu de code ultérieur, j'ai créé un dataframe pandas qui ressemble à l'exemple ci-dessous:

Mean Distance.             No. Of values             Mean rSquared
1                          500                       0.6
(80*2+40*3)/120            (80+40) = 120             (80*0.3+40*0.4)/120
(30*4+50*5+30*6)/110       (30+50+30) = 110          (30*0.2+50*0.2+30*0.1)/110

etc...

Je peux fournir mon code précédent pour créer ce dataframe, mais je ne pensais pas qu'il était particulièrement pertinent.

Je dois additionner le nombre de colonnes de valeurs jusqu'à ce que j'atteigne une valeur> = 100; puis combinez les données des lignes des colonnes adjacentes, en prenant la moyenne pondérée des valeurs de distance et de moyenne r2, comme le montre l'exemple ci-dessous

Distance.     No. of values        Mean rSquared
    1                   500                  0.6
    2                    80                  0.3
    3                    40                  0.4
    4                    30                  0.2
    5                    50                  0.2
    6                    30                  0.1

Je sais que pandas a sa fonction .cumsum , que je pourrais peut-être implémenter dans une boucle for avec une instruction if qui vérifie la limite supérieure et réinitialise la somme à 0 lorsqu'elle est supérieure ou égale à la limite supérieure. Cependant, je ne sais pas comment faire la moyenne des colonnes adjacentes.

Toute aide serait appréciée!


2 commentaires

Avec ce type de seuil, vous feriez mieux d'écrire une boucle for.


Voudriez-vous préciser ce qui devrait entrer dans cette boucle for?


3 Réponses :


2
votes

Vous pouvez utiliser cet extrait de code pour résoudre votre problème.

# First, compute some weighted values
df.loc[:, "weighted_distance"] = df["Distance"] * df["No. of values"]
df.loc[:, "weighted_mean_rSquared"] = df["Mean rSquared"] * df["No. of values"]


min_threshold = 100
indexes = []
temp_sum = 0

# placeholder for final result
final_df = pd.DataFrame()
columns = ["Distance", "No. of values", "Mean rSquared"]

# reseting index to make the 'df' usable in following output
df = df.reset_index(drop=True)

# main loop to check and compute the desired output
for index, _ in df.iterrows():
    temp_sum += df.iloc[index]["No. of values"]
    indexes.append(index)

    # if the sum exceeds 'min_threshold' then do some computation
    if temp_sum >= min_threshold:
        temp_distance = df.iloc[indexes]["weighted_distance"].sum() / temp_sum
        temp_mean_rSquared = df.iloc[indexes]["weighted_mean_rSquared"].sum() / temp_sum
    
        # create temporary dataframe and concatenate with the 'final_df'
        temp_df = pd.DataFrame([[temp_distance, temp_sum, temp_mean_rSquared]], columns=columns)
        final_df = pd.concat([final_df, temp_df])
    
        # reset the variables
        temp_sum = 0
        indexes = []


1 commentaires

Merci beaucoup pour votre réponse, je ne pensais pas que j'allais finir avec une réponse, alors j'ai fini par trébucher en Python et j'ai trouvé une solution similaire, en tant que telle, je vous donne la réponse, merci encore ! Edit: Je n'ai pas initialement terminé le commentaire car je ne savais pas entrer a soumis le commentaire (d'oh)



0
votes

Numpy a une fonction numpy.frompyfunc Vous pouvez l'utiliser pour obtenir la valeur cumulée en fonction d'un seuil.

Voici comment l'implémenter. Avec cela, vous pouvez ensuite déterminer l'indice lorsque la valeur dépasse le seuil. Utilisez-le pour calculer la Mean Distance et la Mean rSquared pour les valeurs de votre trame de données d'origine.

J'ai également exploité l'idée de @ sujanay de calculer d'abord les valeurs pondérées.

   Distance  No. of values  ...  Mean Distance  New Mean rSquared
0         1            500  ...           1.00               0.60
2         3             40  ...           2.33               0.33
5         6             30  ...           5.00               0.17

Le résultat de ceci sera:

final_df = df[df['Mean Distance'].notnull()]

Si vous souhaitez extraire uniquement les enregistrements qui ne sont pas NaN, vous pouvez faire:

   Distance  No. of values  ...  Mean Distance  New Mean rSquared
0         1            500  ...           1.00               0.60
1         2             80  ...            NaN                NaN
2         3             40  ...           2.33               0.33
3         4             30  ...            NaN                NaN
4         5             50  ...            NaN                NaN
5         6             30  ...           5.00               0.17

Cela se traduira par:

c = ['Distance','No. of values','Mean rSquared']
d = [[1,500,0.6], [2,80,0.3], [3,40,0.4],
     [4,30,0.2], [5,50,0.2], [6,30,0.1]]

import pandas as pd
import numpy as np

df = pd.DataFrame(d,columns=c)

#calculate the weighted distance and weighted mean squares first
df.loc[:, "w_distance"] = df["Distance"] * df["No. of values"]
df.loc[:, "w_mean_rSqrd"] = df["Mean rSquared"] * df["No. of values"]

#use numpy.frompyfunc to setup the threshold condition

sumvals = np.frompyfunc(lambda a,b: a+b if a <= 100 else b,2,1)

#assign value to cumvals based on threshold
df['cumvals'] = sumvals.accumulate(df['No. of values'], dtype=np.object)

#find out all records that have >= 100 as cumulative values
idx = df.index[df['cumvals'] >= 100].tolist()

#if last row not in idx, then add it to the list
if (len(df)-1) not in idx: idx += [len(df)-1]

#iterate thru the idx for each set and calculate Mean Distance and Mean rSquared
i = 0
for j in idx:
    df.loc[j,'Mean Distance'] = (df.iloc[i:j+1]["w_distance"].sum() / df.loc[j,'cumvals']).round(2)
    df.loc[j,'New Mean rSquared'] = (df.iloc[i:j+1]["w_mean_rSqrd"].sum() / df.loc[j,'cumvals']).round(2)
    i = j+1

print (df)

J'ai recherché l'implémentation de numpy.frompyfunc par BEN_YO. Le message SO original peut être trouvé ici. Redémarrez cumsum et obtenez l'index si cumsum plus que la valeur


0 commentaires

1
votes

Si vous déterminez d'abord le regroupement, pandas groupby -functionality fera une grande partie du travail restant pour vous. Une boucle est appropriée pour obtenir le regroupement (à moins que quelqu'un n'ait un one-liner intelligent):

>>> df[["Distance.", "Mean rSquared"]].groupby(groups).sum().div(sums, axis=0)
   Distance.  Mean rSquared
0   1.000000       0.600000
1   2.333333       0.333333
2   5.000000       0.172727

Avant d'effectuer les opérations groupées, vous devez utiliser les informations sur le nombre de valeurs pour obtenir la pondération:

>>> sums = df.groupby(groups)["No. of values"].sum()
>>> sums
0    500
1    120
2    110
Name: No. of values, dtype: int64

Maintenant, obtenez les sommes comme ceci:

df[["Distance.", "Mean rSquared"]] = df[["Distance.", "Mean rSquared"]].multiply(df["No. of values"], axis=0)

Et enfin les moyennes pondérées du groupe comme ceci:

>>> groups = []
>>> group = 0
>>> cumsum = 0
>>> for n in df["No. of values"]:
...     if cumsum >= 100:
...         cumsum = 0
...         group = group + 1
...     cumsum = cumsum + n
...     groups.append(group)
>>>
>>> groups
[0, 1, 1, 2, 2, 2]


0 commentaires