Sottocampionamento il numero di voci in una lista (senza interpolazione)
-
26-09-2019 - |
Domanda
Ho una lista Python con un numero di voci, che ho bisogno di downsample utilizzando:
- Un numero massimo di righe. Ad esempio, limitando l'elenco delle voci 1234 e 1000.
- Una parte delle righe originali. Ad esempio, facendo la lista 1/3 della sua lunghezza originale.
(Ho bisogno di essere in grado di fare entrambe le direzioni, ma solo uno viene utilizzato per volta).
Credo che per il numero massimo di righe che posso solo calcolare la proporzione necessaria e passa che al Downsizer proporzionale:
def downsample_to_max(self, rows, max_rows):
return downsample_to_proportion(rows, max_rows / float(len(rows)))
... quindi ho davvero bisogno di una sola funzione downsampling. Eventuali suggerimenti, per favore?
Modifica La lista contiene oggetti, non i valori numerici quindi non ho bisogno di interpolare. La caduta di oggetti va bene.
SOLUZIONE:
def downsample_to_proportion(self, rows, proportion):
counter = 0.0
last_counter = None
results = []
for row in rows:
counter += proportion
if int(counter) != last_counter:
results.append(row)
last_counter = int(counter)
return results
Grazie.
Soluzione
Mantenere un contatore, che si incrementa con il secondo valore. Pavimento ogni volta, e fornire il valore a tale indice.
Altri suggerimenti
È possibile utilizzare islice
da itertools
:
from itertools import islice
def downsample_to_proportion(rows, proportion=1):
return list(islice(rows, 0, len(rows), int(1/proportion)))
Utilizzo:
x = range(1,10)
print downsample_to_proportion(x, 0.3)
# [1, 4, 7]
Invece di islice()
+ list()
è più efficiente utilizzare la sintassi slice direttamente se l'ingresso è già un tipo di sequenza:
def downsample_to_proportion(rows, proportion):
return rows[::int(1 / proportion)]
Questa soluzione potrebbe essere un po 'eccessivo per il manifesto originale, ma ho pensato di condividere il codice che ho usato per risolvere questo e simili problemi.
E 'un po' lungo (circa 90 righe), ma se si ha spesso questa esigenza, vogliono un oneliner facile da usare, e hanno bisogno di un ambiente privo di dipendenza puro-Python, allora mi sa che potrebbe essere utile.
In sostanza, l'unica cosa che dovete fare è passare la vostra lista per la funzione e indicare cosa lunghezza si desidera che il nuovo elenco di essere, e la funzione di due operazioni:
- downsize l'elenco facendo cadere oggetti se la nuova lunghezza è più piccolo, molto simile alle risposte precedenti già suggerito.
- tratto / alto livello la vostra lista (l'opposto di downsizing) se la nuova lunghezza è più grande, con l'opzione aggiuntiva che si può decidere se:
- linearmente interpolare bw i valori noti (scelti automaticamente se elenco contiene interi o galleggianti)
- duplicare ogni valore in modo che occupano una dimensione proporzionale della nuova lista (scelto automaticamente se l'elenco contiene i non-numeri)
- tirare i valori originali a parte e le lacune congedo tra
Il tutto viene raccolto all'interno di una funzione, quindi se avete bisogno basta copiare e incollare al vostro script e si può iniziare ad usarlo subito.
Per esempio si potrebbe dire:
origlist = [0,None,None,30,None,50,60,70,None,None,100]
resizedlist = ResizeList(testlist, 21)
print(resizedlist)
e get
[0, 5.00000000001, 9.9999999999900009, 15.0, 20.000000000010001, 24.999999999989999, 30, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70, 75.000000000010004, 79.999999999989996, 85.0, 90.000000000010004, 94.999999999989996, 100]
Si noti che lievi imprecisioni potranno verificarsi a causa di limitazioni virgola mobile. Inoltre, ho scritto questo per Python 2.x, quindi usarlo su Python 3.x è sufficiente aggiungere una sola riga che dice xrange = range
.
E qui è un trucco ingegnoso per interpolare tra elementi secondari posizionati in una lista di liste. Così, per esempio, si può facilmente interpolare tra tuple di colore RGB per creare una sfumatura di colore di x n di passi. Assumendo una lista di tuple RGB a colori di 3 e una variabile GRADIENTLENGTH desiderato si esegue questa operazione con:
crosssections = zip(*rgbtuples)
grad_crosssections = ( ResizeList(spectrum,GRADIENTLENGTH) for spectrum in crosssections )
rgb_gradient = [list(each) for each in zip(*grad_crosssections)]
Probabilmente potrebbe avere bisogno di un bel paio di ottimizzazioni, ho dovuto fare un po 'di sperimentazione. Se vi sentite in grado di migliorare si sente libero di modificarla mio post. Ecco il codice:
def ResizeList(rows, newlength, stretchmethod="not specified", gapvalue=None):
"""
Resizes (up or down) and returns a new list of a given size, based on an input list.
- rows: the input list, which can contain any type of value or item (except if using the interpolate stretchmethod which requires floats or ints only)
- newlength: the new length of the output list (if this is the same as the input list then the original list will be returned immediately)
- stretchmethod: if the list is being stretched, this decides how to do it. Valid values are:
- 'interpolate'
- linearly interpolate between the known values (automatically chosen if list contains ints or floats)
- 'duplicate'
- duplicate each value so they occupy a proportional size of the new list (automatically chosen if the list contains non-numbers)
- 'spread'
- drags the original values apart and leaves gaps as defined by the gapvalue option
- gapvalue: a value that will be used as gaps to fill in between the original values when using the 'spread' stretchmethod
"""
#return input as is if no difference in length
if newlength == len(rows):
return rows
#set auto stretchmode
if stretchmethod == "not specified":
if isinstance(rows[0], (int,float)):
stretchmethod = "interpolate"
else:
stretchmethod = "duplicate"
#reduce newlength
newlength -= 1
#assign first value
outlist = [rows[0]]
writinggapsflag = False
if rows[1] == gapvalue:
writinggapsflag = True
relspreadindexgen = (index/float(len(rows)-1) for index in xrange(1,len(rows))) #warning a little hacky by skipping first index cus is assigned auto
relspreadindex = next(relspreadindexgen)
spreadflag = False
gapcount = 0
for outlistindex in xrange(1, newlength):
#relative positions
rel = outlistindex/float(newlength)
relindex = (len(rows)-1) * rel
basenr,decimals = str(relindex).split(".")
relbwindex = float("0."+decimals)
#determine equivalent value
if stretchmethod=="interpolate":
#test for gap
maybecurrelval = rows[int(relindex)]
maybenextrelval = rows[int(relindex)+1]
if maybecurrelval == gapvalue:
#found gapvalue, so skipping and waiting for valid value to interpolate and add to outlist
gapcount += 1
continue
#test whether to interpolate for previous gaps
if gapcount > 0:
#found a valid value after skipping gapvalues so this is where it interpolates all of them from last valid value to this one
startvalue = outlist[-1]
endindex = int(relindex)
endvalue = rows[endindex]
gapstointerpolate = gapcount
allinterpolatedgaps = Resize([startvalue,endvalue],gapstointerpolate+3)
outlist.extend(allinterpolatedgaps[1:-1])
gapcount = 0
writinggapsflag = False
#interpolate value
currelval = rows[int(relindex)]
lookahead = 1
nextrelval = rows[int(relindex)+lookahead]
if nextrelval == gapvalue:
if writinggapsflag:
continue
relbwval = currelval
writinggapsflag = True
else:
relbwval = currelval + (nextrelval - currelval) * relbwindex #basenr pluss interindex percent interpolation of diff to next item
elif stretchmethod=="duplicate":
relbwval = rows[int(round(relindex))] #no interpolation possible, so just copy each time
elif stretchmethod=="spread":
if rel >= relspreadindex:
spreadindex = int(len(rows)*relspreadindex)
relbwval = rows[spreadindex] #spread values further apart so as to leave gaps in between
relspreadindex = next(relspreadindexgen)
else:
relbwval = gapvalue
#assign each value
outlist.append(relbwval)
#assign last value
if gapcount > 0:
#this last value also has to interpolate for previous gaps
startvalue = outlist[-1]
endvalue = rows[-1]
gapstointerpolate = gapcount
allinterpolatedgaps = Resize([startvalue,endvalue],gapstointerpolate+3)
outlist.extend(allinterpolatedgaps[1:-1])
outlist.append(rows[-1])
gapcount = 0
writinggapsflag = False
else:
outlist.append(rows[-1])
return outlist
Impossibile random.choices () risolvere il problema? Altri esempi sono disponibili qui