2012-03-26 1 views
7

Ich habe einen Vektor/Array von n Elementen. Ich möchte m Elemente auswählen.Wählen Sie m gleichmäßig beabstandete Elemente aus einer Sequenz der Länge n

Die Auswahl muss fair/deterministisch sein - ebenso viele von jedem Unterabschnitt.

Mit m = 10, n = 20 ist es einfach: nehmen Sie einfach jedes zweite Element. Aber wie geht es im allgemeinen Fall? Muss ich das LCD berechnen?

+12

, was ist falsch mit der ersten Wahl ' m' Elemente? es scheint, dass es eine Einschränkung gibt, die Sie implizieren, ist dort, aber Sie haben es nicht beschrieben. –

+2

Willst du 'm'-Positionen gleichmäßig über' n' verteilen? – hamstergene

+0

Danke. Es muss fair sein - ich brauche gleich viele von jedem Unterabschnitt - d. H. Von jedem Teil des ursprünglichen Arrays. Es muss verteilt werden. – j13r

Antwort

9

Hier ist ein kurzes Beispiel:

from math import ceil 

def takespread(sequence, num): 
    length = float(len(sequence)) 
    for i in range(num): 
     yield sequence[int(ceil(i * length/num))] 

math.ceil, weil ohne sie verwendet wird, werden die ausgewählten Indizes zu viel zu Beginn jeden impliziten Unterabschnittes gewichtet werden, und als Ergebnis wird die Liste als Ganze.

+0

Warum brauchen wir hier ceil? Würde die int-Kürzung nicht die Aufgabe erfüllen, d. H. Nur die Reihenfolge [i * length/num] – j13r

+0

@ j13r Die Objekte werden zu sehr am Anfang der Liste gewichtet, wenn Sie den impliziten 'floor' verwenden. – agf

+0

würde nicht mehr Sinn ergeben? – j13r

17

Sie benötigen wahrscheinlich Bresenham's line algorithm. Die gleichförmige Auswahl von m Elementen aus n entspricht dem Zeichnen einer Linie in m x n diskreten Pixelraster. Nehmen wir x Koordinaten in 0 .. n-1 und y Koordinaten 0 .. m-1, und fahren Sie fort, wenn Sie eine Linie zwischen (0,0) und (n-1, m-1) zeichnen. Wenn sich y Koordinaten ändern, wählen Sie ein Element aus dem Index x.

UPD: Aber es scheint, dass diese einfache Funktion, die Sie genügen:

>>> f = lambda m, n: [i*n//m + n//(2*m) for i in range(m)] 
>>> f(1,20) 
[10] 
>>> f(2,20) 
[5, 15] 
>>> f(3,20) 
[3, 9, 16] 
>>> f(5,20) 
[2, 6, 10, 14, 18] 
+0

Da '//' auch auf Python 2 funktioniert, ist es besser, explizit zu sein und das zu verwenden, wenn Sie Division abschneiden wollen. – agf

+0

@agf Tatsächlich. Aktualisiert. – hamstergene

1

eine Schleife verwenden (int i = 0; i < m; i ++)

dann die Indizes, die Sie wollen , Ceil (i * m/n).

0

Ich arbeite an einer klinischen Anwendung und fand alle oben genannten Antworten unterschiedlich voreingenommen. Hier ist eine andere Lösung, die auch im Kreis gut funktioniert. Das heißt, auch wenn die letzte Zahl umgeht wie bei der Arbeit mit Grad 0 ° = 360 °.

import numpy as np 
m = 51 
# Generate intervals 
epts = np.linspace(0,360,m+1,endpoint=True) 
# Create the halfsteps between intervals (One would have sufficed) 
halfsteps = (epts[1:] - epts[:-1])/2 
# Find the midpoints 
midpoints = epts[:-1] + halfsteps 
# Make an unbiased rounding 
results = np.around(midpoints, decimals=0) 
+0

Sie könnten einfach Mittelpunkte mit 'midpoints = (epts [1:] + epts [: - 1])/2 'berechnen, sollten das gleiche oder bessere Ergebnis haben, wenn Sie bedenken, dass' lamsteps' möglicherweise zu klein ist, wenn m zu groß ist – AngelLeliel

0

Dadurch werden die ersten und letzten Elemente immer wählen:

which_idxs = lambda m, n: np.rint(np.linspace(1, n, min(m,n)) - 1).astype(int) 

evenly_spaced = np.array(your_list)[which_idxs(m,n)] 

Dies wird maximal n Elemente nur auszuwählen, falls m größer als n ist. Wenn Sie wirklich über das Array verteilt es gleichmäßig wollen, auch an den Enden, dann wäre es diese stattdessen sein:

which_idxs = lambda m, n: [idx for idx in np.rint(np.linspace(1-n/(2*min(m,n)), n+n/(2*min(m,n)), min(m,n)+2) - 1).astype(int) if idx in range(n)] 

evenly_spaced = np.array(your_list)[which_idxs(m,n)] 

Was Sie so etwas wie dieses gibt:

>>> np.array([1, 2, 3, 'a', 'b', 'c'])[which_idxs(m,n)] 
Out: array(['2', 'b']) 
Verwandte Themen