Obtenez des secondes valeurs minimales par colonne dans un tableau 2D

15

Comment puis-je obtenir la deuxième valeur minimale de chaque colonne? J'ai ce tableau:

A = [[72 76 44 62 81 31]
     [54 36 82 71 40 45]
     [63 59 84 36 34 51]
     [58 53 59 22 77 64]
     [35 77 60 76 57 44]]

Je souhaite avoir une sortie comme:

A = [54 53 59 36 40 44]
Mr Dan
la source
as-tu essayé quelque chose? ?
Meha Parekh
deuxième minimum par colonne ?
Nicolas Gervais
@NicolasGervais yes
Mr Dan

Réponses:

12

Essayez ceci, en une seule ligne:

[sorted(i)[1] for i in zip(*A)]

en action:

In [12]: A = [[72, 76, 44, 62, 81, 31], 
    ...:      [54 ,36 ,82 ,71 ,40, 45], 
    ...:      [63 ,59, 84, 36, 34 ,51], 
    ...:      [58, 53, 59, 22, 77 ,64], 
    ...:      [35 ,77, 60, 76, 57, 44]] 

In [18]: [sorted(i)[1] for i in zip(*A)]                                                                                                                                                                           
Out[18]: [54, 53, 59, 36, 40, 44]

zip(*A) transposera votre liste de liste afin que les colonnes deviennent des lignes.

et si vous avez une valeur en double, par exemple:

In [19]: A = [[72, 76, 44, 62, 81, 31], 
    ...:  [54 ,36 ,82 ,71 ,40, 45], 
    ...:  [63 ,59, 84, 36, 34 ,51], 
    ...:  [35, 53, 59, 22, 77 ,64],   # 35
    ...:  [35 ,77, 50, 76, 57, 44],]  # 35

Si vous devez ignorer les deux 35, vous pouvez utiliser set():

In [29]: [sorted(list(set(i)))[1] for i in zip(*A)]                                                                                                                                                                
Out[29]: [54, 53, 50, 36, 40, 44]
Mehrdad Pedramfar
la source
6

Les opérations sur les numpytableaux doivent être effectuées avec des numpyfonctions, alors regardez celle-ci:

np.sort(A, axis=0)[1, :]
Out[61]: array([54, 53, 59, 36, 40, 44])
Nicolas Gervais
la source
Ce doit être la meilleure solution pour autant que je sache, elle garde tout en place numpy, je pense que la solution lambdadoit ralentir heapq.nsmallest. Il semble préférable de tout garder rapidenumpy
jamylak
3

vous pouvez utiliser heapq.nsmallest

from heapq import nsmallest

[nsmallest(2, e)[-1] for e in zip(*A)]

production:

[54, 53, 50, 36, 40, 44]

J'ai ajouté un benchmark simple pour comparer les performances des différentes solutions déjà publiées:

entrez la description de l'image ici

from simple_benchmark import BenchmarkBuilder
from heapq import nsmallest


b = BenchmarkBuilder()

@b.add_function()
def MehrdadPedramfar(A):
    return [sorted(i)[1] for i in zip(*A)]

@b.add_function()
def NicolasGervais(A):
    return np.sort(A, axis=0)[1, :]

@b.add_function()
def imcrazeegamerr(A):
    rotated = zip(*A[::-1])

    result = []
    for arr in rotated:
        # sort each 1d array from min to max
        arr = sorted(list(arr))
        # add the second minimum value to result array
        result.append(arr[1])

    return result

@b.add_function()
def Daweo(A):
    return np.apply_along_axis(lambda x:heapq.nsmallest(2,x)[-1], 0, A)

@b.add_function()       
def kederrac(A):
    return [nsmallest(2, e)[-1] for e in zip(*A)]


@b.add_arguments('Number of row/cols (A is  square matrix)')
def argument_provider():
    for exp in range(2, 18):
        size = 2**exp
        yield size, [[randint(0, 1000) for _ in range(size)] for _ in range(size)]

r = b.run()
r.plot()

Utiliser zipavec la sortedfonction est la solution la plus rapide pour les petites listes 2D tout en utilisant zipavec les heapq.nsmallestémissions pour être le meilleur sur les grandes listes 2D

kederrac
la source
1
Juste une pensée sauvage: ces résultats peuvent-ils être affectés par le fait que vous avez généré des nombres qui ne sont pas des dtypes numpy? De plus, le randint intégré ne retournera-t-il pas une liste au lieu d'un tableau?
Nicolas Gervais
1

J'espère avoir bien compris votre question, mais dans tous les cas, voici ma solution, je suis sûr qu'il existe une façon plus élégante de le faire, mais cela fonctionne

A = [[72,76,44,62,81,31]
 ,[54,36,82,71,40,45]
 ,[63,59,84,36,34,51]
 ,[58,53,59,22,77,64]
 ,[35,77,50,76,57,44]]

#rotate the array 90deg
rotated = zip(*A[::-1])

result = []
for arr in rotated:
    # sort each 1d array from min to max
    arr = sorted(list(arr))
    # add the second minimum value to result array
    result.append(arr[1])
print(result)

entrez la description de l'image ici

imcrazeegamer
la source
0

En supposant que ce Asoit le numpy.arraycas (si cela se vérifie, envisagez d'ajouter une numpybalise à votre question), vous pouvez utiliser la apply_along_axisméthode suivante:

import heap
import numpy as np
A = np.array([[72, 76, 44, 62, 81, 31],
              [54, 36, 82, 71, 40, 45],
              [63, 59, 84, 36, 34, 51],
              [58, 53, 59, 22, 77, 64],
              [35, 77, 60, 76, 57, 44]])
second_mins = np.apply_along_axis(lambda x:heapq.nsmallest(2,x)[-1], 0, A)
print(second_mins)  # [54 53 59 36 40 44]

Notez que j'ai utilisé heapq.nsmallest car il fait autant de tri que nécessaire pour obtenir 2 plus petits éléments, contrairement à sortedce qui fait le tri complet.

Daweo
la source
0
>>> A = np.arange(30).reshape(5,6).tolist()
>>> A
[[0, 1, 2, 3, 4, 5], 
 [6, 7, 8, 9, 10, 11], 
 [12, 13, 14, 15, 16, 17], 
 [18, 19, 20, 21, 22, 23],
 [24, 25, 26, 27, 28, 29]]

Mise à jour : utiliser setpour empêcher la duplication et la transposition de la liste à l'aidezip(*A)

>>> [sorted(set(items))[1] for items in zip(*A)]
[6, 7, 8, 9, 10, 11]

ancien: deuxième élément minimum dans chaque ligne

>>> [sorted(set(items))[1] for items in A]
[1, 7, 13, 19, 25]
Dishin H Goyani
la source
N'est-ce pas obtenir le deuxième élément de chaque ligne plutôt que colonne?
paxdiablo
@paxdiablo Oui merci pour l'informer. réponse mise à jour.
Dishin H Goyani