J'ai une grande trame de données avec 1739 lignes et 1455 colonnes. Je veux trouver les 150 valeurs les plus basses pour chaque ligne (pas la 150 e valeur mais 150 valeurs).
Je parcours les lignes avec une boucle for basique.
J'ai essayé df.min (axis = 1)
mais cela ne donne qu'une minute. Et aussi la fonction rolling_min
sans succès.
Existe-t-il une fonction où je peux entrer le nombre de valeurs que je veux trouver avec .min?
Mon but ultime est de prendre les 150 valeurs les plus basses et de créer une pente puis de calculer l'aire sous la courbe. Faites ceci pour chaque ligne et ajoutez les zones pour obtenir un volume.
Exemple de la dataframe, j'ai un df qui ressemble à ceci:
-218.7 -218.4 ... for 150 columns 4 -18.532035 -18.497517 ... for 150 values
L'en-tête est le '-218.7 ...' qui sont les coordonnées sur l'axe x d'un scan. Les données correspondent à la hauteur du scan sur l'axe y. Ce dont j'ai besoin, ce sont les 150 valeurs les plus basses pour chaque ligne et l'en-tête de colonne associé car je veux créer une courbe pour chaque ligne, puis calculer l'aire sous la courbe.
J'ai donc besoin pour chaque ligne de quelque chose comme ceci:
-218.7 -218.4 ... 217.2 217.5 0 56.632706 13.638315 ... 76.543000 76.543000 1 56.633455 13.576762 ... 76.543000 76.543000 2 -18.432203 -18.384091 ... 76.543000 76.543000 3 -18.476594 -18.439804 ... 76.543000 76.543000
Je ne pense pas avoir besoin de stocker les informations d'en-tête pour chaque ligne, un pour boucle passerait par chaque ligne une à la fois.
5 Réponses :
Je ne connais pas de moyen de le faire sans parcourir les lignes:
df = df.transpose() for col in df.columns: min_values = df[col].sort_values()[0:150] # now calc slope/area
Pourquoi transposez-vous df? Existe-t-il un moyen de parcourir les lignes à la place?
Vous pouvez parcourir les lignes en utilisant: for i, row dans df.iterrows () si vous le souhaitez. Vous auriez à transformer la ligne en un np.array je pense.
Si je comprends bien, la question se résume à obtenir les k plus petits nombres dans une liste de M (> k) nombres. Ceci doit ensuite être appliqué à chaque ligne individuellement.
Si numpy est disponible et que l'ordre n'a pas d'importance, vous pouvez essayer d'utiliser argpartition : Avec le paramètre donné k, il partitionne un tableau de manière à ce que le kème élément soit placé dans sa position triée, tous les nombres plus petits sont avant, tous les nombres plus grands derrière (dans un ordre non spécifié):
import numpy as np data = np.array([ [1, 6, 2, 12, 7, 8, 9, 11, 15, 26], [1, 65, 2, 12, 7, 8, 9, 11, 15, 26], [16, 6, 2, 12, 7, 8, 9, 11, 15, 26]]) k = 5 idx = np.argpartition(data, k)[:,:k] print(idx) --> [[1 0 2 4 5] [2 0 4 5 6] [4 2 1 5 6]]
Modifier : Cela fonctionne également en ligne / sage pour les tableaux complets: p >
import numpy as np row = np.array([1, 6, 2, 12, 7, 8, 9, 11, 15, 26]) k = 5 idx = np.argpartition(row, k)[:k] print(idx) print(row[idx]) --> [1 0 2 4 5] [6 1 2 7 8]
Cela fonctionnerait s'il y avait un moyen de les mettre en ordre, j'en ai besoin pour construire la courbe après cela ...
@ Mat17 Que voulez-vous dire par «faites-les dans l'ordre»? - Trier les 150 entrées par ligne par la suite ne serait pas un problème, je pense. Si vous avez besoin de la commande d'origine, vous devrez trier idx avant de l'utiliser.
Si vous parcourez le df en utilisant une boucle for comme vous le mentionnez dans la question, vous pouvez simplement faire ceci:
import numpy import pandas import random # generate dummy data l = list(range(1600)) random.shuffle(l) a = numpy.array(l) a = a.reshape(40, 40) # columns x rows dummy_df = pandas.DataFrame(a) # dummy_df.shape = (40, 40) smallest = [] for idx, row in dummy_df.iterrows(): smallest.append(sorted(row.values)[:10]) new_df = pandas.DataFrame(numpy.array(smallest)) # new_df.shape = (40, 10)
Test:
for index, row in df.iterrows(): # your loop new_row = sorted(row.values)[:150] # new_row should be a list with length 150.
Vous pouvez utiliser heapq.nsmallest
pour trouver les n
plus petits nombres dans une liste. Cela peut être rapidement appliqué à chaque ligne d'un dataframe en utilisant .apply
:
smallest_df.head()
Chaque ligne de la plus petite est maintenant une liste des 150 plus petites valeurs de la ligne correspondante dans df
.
Ceci peut être converti en un dataframe en utilisant:
smallest_df = pd.DataFrame(smallest.values.tolist())
Ceci est maintenant un dataframe où chaque ligne correspond à chaque ligne dans le dataframe d'origine. Il y a 150 colonnes, avec les 150 plus petites valeurs dans chaque ligne de l'original.
import pandas as pd import numpy as np import heapq df = pd.DataFrame(np.random.randn(1000, 1000)) # Find the 150 smallest values in each row smallest = df.apply(lambda x: heapq.nsmallest(150, x), axis=1)
Cela fonctionnerait parfaitement si je pouvais garder l'en-tête de colonne d'origine lié, car c'est la position sur l'axe des x et les 150 valeurs sont les hauteurs sur l'axe des y. @willk
Juste pour que je comprends: vous voulez les colonnes où les valeurs les plus basses se produisent pour chaque ligne ainsi que les valeurs elles-mêmes? Pourriez-vous faire un petit exemple du dataframe final? Je peux probablement modifier cette réponse.
J'ai ajouté les informations dans la question, veuillez voir ci-dessus et merci pour l'aide.
Utilisez .argsort
pour obtenir le tri des indices du tableau sous-jacent. Découpez les valeurs et la colonne Index pour obtenir toutes les informations dont vous avez besoin. Nous allons créer un MultiIndex afin de pouvoir stocker à la fois les en-têtes de colonne et les valeurs dans le même DataFrame. Le premier niveau sera votre index d'origine.
0 1 2 3 4 5 6 Columns 0 C K U V I G P Value 0 5193 7752 8445 19947 20610 21441 21759 Columns 1 R J W C B D G Value 1 432 3607 16278 17138 19434 26104 33879 Columns 2 K S E F M O U Value 2 16 1047 1845 9553 12314 13784 19432 Columns 3 K Q A S X W G Value 3 244 5272 10836 13682 29237 33230 34448 Columns 4 K T L U C D M Value 4 9765 11275 13160 22808 30870 33484 42760 ... Columns 1736 I L U W B X N Value 1736 7099 7922 11047 12613 15502 18409 21576 Columns 1737 J K E V O M S Value 1737 3000 5538 7933 13777 17310 22640 24750 Columns 1738 O U A X E I F Value 1738 5118 5775 10267 11320 17659 30055 30702
import pandas as pd import numpy as np np.random.seed(1) df = pd.DataFrame(np.random.randint(1,100000, (1739, 26))) df.columns = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') N = 7 # 150 in your case idx = np.argsort(df.values, 1)[:, 0:N] pd.concat([pd.DataFrame(df.values.take(idx), index=df.index), pd.DataFrame(df.columns[idx], index=df.index)], keys=['Value', 'Columns']).sort_index(level=1)
@ALollz Merci, cela fonctionne très bien! J'ai juste besoin de trouver chaque courbe et leur aire en dessous et je suis prêt à partir!
Avez-vous une idée de la façon de créer une courbe avec les colonnes 0 et la valeur 0? J'ai essayé un plt.scatter basique mais ça ne marche pas?
@ Mat17 Je pense que c'est suffisamment différent pour justifier une nouvelle question . Cela impliquera probablement un certain groupby
ou un découpage du MultiIndex
mais cela dépend simplement si vous voulez tous les graphiques ou si vous n'avez besoin d'en regarder que quelques-uns à la fois.
pouvez-vous trier le tableau? Si tel est le cas, essayez de le trier, de l'enregistrer dans un tableau temporaire et d'obtenir une sous-liste de 150 éléments
Juste pour clarifier pour ma propre tête, vous voulez les 150 plus petites valeurs dans chaque ligne, pour les 1739 lignes?
@Talik ouais cela pourrait fonctionner si je peux trier le tableau. Mais alors je devrais le trier 1739 fois (une pour chaque ligne).
Non, il vous suffit de trier le long du 1er axe:
np.sort (df.values, 1) [:, 0: 150]
@d_kennetz oui c'est exactement ce dont j'ai besoin.
Si vous vous souciez de l'efficacité, vous devriez utiliser une recherche linéaire au lieu du tri. Il suffit de parcourir chaque ligne et de stocker les 150 plus petites valeurs dans une liste; cela prendra O (lignes * colonnes) alors que le tri prendrait O ((lignes * colonnes) * lg (lignes * colonnes)).