2012-03-26 15 views
7

Ho un vettore/array di n elementi. Voglio scegliere m elementi.Scegliere m elementi equidistanti da una sequenza di lunghezza n

Le scelte devono essere corrette/deterministiche - ugualmente molte da ciascuna sottosezione.

Con m = 10, n = 20 è facile: basta prendere ogni secondo elemento. Ma come si fa nel caso generale? Devo calcolare il display LCD?

+12

cosa è sbagliato con la scelta del primo ' m' elementi? sembra che ci sia qualche limite che stai insinuando c'è, ma non l'hai descritto. –

+2

Intendi assumere posizioni 'm' distribuite uniformemente su' n'? – hamstergene

+0

Grazie. Deve essere giusto - ho bisogno di ugualmente molti da ogni sottosezione - cioè da ogni parte dell'array originale. Deve essere distribuito. – j13r

risposta

9

Ecco un breve esempio:

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 viene usato perché senza di essa, gli indici scelti vengono ponderati troppo verso l'inizio di ogni sottosezione implicita, e come risultato la lista nel suo complesso.

+0

Perché abbiamo bisogno di ceil qui? Non sarebbe il troncamento int eseguire il lavoro, cioè solo la sequenza di rendimento [i * lunghezza/numero] – j13r

+0

@ j13r Gli oggetti saranno pesati verso l'inizio della lista troppo se si utilizza il 'pavimento' implicito. – agf

+0

non avrebbe senso avere più senso allora? – j13r

17

Probabilmente è necessario Bresenham's line algorithm. Scegliere gli elementi m in modo uniforme da n equivale a disegnare una riga nella griglia di pixel discreti m x n. Assumere la coordinatain 0 .. n-1 e y coordinata 0 .. m-1 e procedere come se si stesse disegnando una linea tra (0,0) e (n-1, m-1). Ogni volta che le coordinate y cambiano, selezionare un elemento dall'indice x.

UPD: Ma sembra che questa semplice funzione sarà sufficiente si:

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

Poiché '//' funziona anche su Python 2 è meglio essere espliciti e usarli quando si intende troncare la divisione. – agf

+0

@agf Effettivamente. Aggiornato. – hamstergene

1

utilizzare un ciclo (int i = 0; i < m; i ++)

Poi per ottenere gli indici che si desidera , Ceil (i * m/n).

0

Sto lavorando a un'applicazione clinica e ho trovato tutte le risposte sopra per avere diversi gradi di bias. Ecco un'altra soluzione che funziona bene anche in un cerchio. Cioè, anche se l'ultimo numero si avvolge come in quando lavori con gradi 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

potresti semplicemente calcolare i punti medi con 'midpoints = (epts [1:] + epts [: - 1])/2', dovrebbe avere lo stesso risultato o migliore se consideri che' halfsteps' potrebbe essere troppo piccolo quando m è troppo grande – AngelLeliel

0

Questo sarà sempre selezionare il primo e l'ultimo elemento:

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

Ciò selezionare solo un massimo di n elementi, nel caso in cui m è maggiore di n. Se veramente si vuole ugualmente diffuse in tutta la matrice, anche alle estremità, allora sarebbe questo, invece:

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

che ti dà qualcosa di simile:

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