5
votes

Recherche des n valeurs les plus basses pour chaque ligne d'une trame de données

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.


6 commentaires

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)).


5 Réponses :


0
votes

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


2 commentaires

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.



1
votes

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]


2 commentaires

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.



0
votes

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.


0 commentaires

1
votes

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)

 smallest_df


3 commentaires

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.



3
votes

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.

Exemple:

                 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

Sortie:

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)


3 commentaires

@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.