15
votes

Obtenir les secondes valeurs minimales par colonne dans un tableau 2D

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

A = [54 53 59 36 40 44]

Je souhaite avoir une sortie comme:

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]]


1 commentaires

deuxième minimum par colonne ?


6 Réponses :


12
votes

Essayez ceci, en une seule ligne:

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

en action:

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

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 [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]

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

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


0 commentaires

0
votes
>>> [sorted(set(items))[1] for items in A]
[1, 7, 13, 19, 25]

1 commentaires

N'est-ce pas obtenir le deuxième élément de chaque ligne plutôt que la colonne?



6
votes

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

Out[61]: array([54, 53, 59, 36, 40, 44])
np.sort(A, axis=0)[1, :]


1 commentaires

Cela doit être la meilleure solution pour autant que je sache, cela garde tout dans numpy , je pense que le lambda doit ralentir la solution heapq.nsmallest . Il semble préférable de tout garder rapide et numpy



1
votes

J'espère avoir bien compris votre question, mais dans tous les cas, voici ma solution, je suis sûr qu'il existe une manière 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


0 commentaires

0
votes

En supposant que A est numpy.array (si cela est vrai, pensez à ajouter numpy balise numpy à votre question), vous pouvez utiliser apply_along_axis de la manière 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 effectue autant de tri que nécessaire pour obtenir les 2 plus petits éléments, contrairement à sorted qui effectue un tri complet.


0 commentaires

4
votes

vous pouvez utiliser heapq.nsmallest

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

production:

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

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

entrez la description de l'image ici

from heapq import nsmallest

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

L'utilisation de zip avec une fonction sorted est la solution la plus rapide pour les petites listes heapq.nsmallest tout en utilisant zip avec heapq.nsmallest montre qu'elle est la meilleure sur les grandes listes heapq.nsmallest


2 commentaires

Juste une idée folle: 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?


est-ce la seule façon d'itérer sur les lignes de np.matrix? existe-t-il une alternative plus rapide?