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!
3 Réponses :
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 = []
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)
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
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]
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?