Ci-dessous est mon dataframe

df = pd.DataFrame({
                   'Year': [2021, 2022, 2023, 2024, 2025],
                    'Tval' : [1, 9, 8, 1, 6]
})

Je veux créer une nouvelle colonne avec la sortie comme indiqué dans le snap attaché.

Dans snap one, les multiplicateurs (2,3,1,2,1,3,2,6 et 1,13) sont générés aléatoirement. de même pour deux et trois.

Quel est le moyen le plus efficace d'effectuer cette opération car c'est une version simplifiée du problème d'origine (qui a plus de 30k lignes). Pourrait utiliser la boucle mais ça va être très, très inefficace.

enter image description here

1
user13412850 5 nov. 2020 à 19:53

3 réponses

Meilleure réponse

Vous voulez que la valeur de chaque ligne soit le produit des lignes suivantes avec des valeurs aléatoires (valeurs aléatoires recalculées pour chaque opération). Vous pouvez le faire comme suit:

values = df.sort_index(ascending=False)['Tval']
values = values.expanding().apply(lambda x: np.sum(x*np.random.random(size=len(x))))
df["values"] = values

Résultat:

   Year  Tval     values
0  2021     1  10.342499
1  2022     9  15.595990
2  2023     8  11.491088
3  2024     1   5.447966
4  2025     6   3.689064

Explication:

  • inverser l'ordre des lignes afin que expanding opère sur toutes les lignes pour le premier index, une ligne pour le dernier
  • applique expanding() à la somme des lignes d'index supérieur ou égal, pondérée de manière aléatoire. Les poids sont recalculés à chaque itération.
  • ajoute des "valeurs" au dataframe d'origine (l'affectation / la jointure se fait sur la valeur d'index, pas besoin de trier la série avant de l'ajouter à df)

Pour vérifier la cohérence, supprimez la pondération aléatoire et observez que cela se réduit à une opération Cumsum inversée:

values = df.sort_index(ascending=False)['Tval']
values = values.expanding().apply(sum)
df["values"] = values

Une solution similaire peut être utilisée si les poids ne doivent pas changer entre les itérations. Comme le suggère l'une des autres solutions, vous pouvez également pré-calculer tous les poids aléatoires et prendre un produit interne. Ce sera une mémoire inefficace mais peut être beaucoup plus rapide, car apply n'est pas vectorisé.

1
anon01 6 nov. 2020 à 18:20

Si vous avez prédéfini une liste / un tableau numpy de nombres aléatoires, multipliez simplement comme ceci:

df['Year'] = df['Year'].values*myList

Si vous avez besoin de valeurs générées aléatoirement, utilisez numpy, par exemple:

df['Year']=df['Year'].values*np.abs((np.random.randn(1,5)*2).round(2))
-2
Wasif Hasan 5 nov. 2020 à 16:58

L'opération que vous effectuez est un produit scalaire, dans lequel vous pouvez tenir compte de l'utilisation décrémentielle des données en définissant des pondérations sur 0

weights = np.random.rand(5, 5)
weights = np.tril(weights)

print(weights)
[[0.80446016 0.         0.         0.         0.        ]
 [0.38560755 0.45014049 0.         0.         0.        ]
 [0.61068876 0.91918189 0.66418596 0.         0.        ]
 [0.78442001 0.63551564 0.35635216 0.14712083 0.        ]
 [0.54315584 0.20083916 0.28262627 0.01919842 0.58714358]]

Le produit scalaire correspondra à la première ligne de weights, le multipliera par les valeurs de df["Tval"], puis additionnera chacun de ces produits. Ensuite, il prendra la 2ème ligne de weights et fera la même chose, mais puisque nous définissons la première valeur de la 2ème ligne de poids sur 0, nous ignorerons essentiellement la première valeur de df["Tval"] et multiplierons / additionner le reste des valeurs. Etc., etc.

df["value"] = df["Tval"] @ weights
print(df)
   Year  Tval      value
0  2021     1  19.181775
1  2022     9  11.324420
2  2023     8   7.936429
3  2024     1   5.792162
4  2025     6   5.243747
1
anon01 6 nov. 2020 à 18:24