2010-06-10 15 views
6

tengo una lista Python con un número de entradas, que necesito disminuir la resolución utilizando:Downsampling el número de entradas en una lista (sin interpolación)

  • Un número máximo de filas. Por ejemplo, limitar una lista de 1234 entradas a 1000.
  • Una proporción de las filas originales. Por ejemplo, hacer que la lista 1/3 tenga su longitud original.

(Necesito poder hacer las dos cosas, pero solo se usa una a la vez).

Creo que para el número máximo de filas sólo puedo calcular la proporción necesaria y pasar a la que downsizer proporcional:

def downsample_to_max(self, rows, max_rows): 
     return downsample_to_proportion(rows, max_rows/float(len(rows))) 

... así que realmente sólo necesita una función de la disminución de resolución. Cualquier sugerencia, por favor?

EDIT: La lista contiene objetos, no valores numéricos, por lo que no es necesario interpolar. Dejar caer objetos está bien.

SOLUCIÓN:

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 

Gracias.

Respuesta

0

Mantenga un contador, que se incrementa por el segundo valor. Piso cada vez, y ceda el valor en ese índice.

+0

¿Puede elaborar un poco? Gracias. – Dave

+0

Comience con un contador en 0. Mientras que el contador es menor que la longitud de la lista: ceda el elemento de la lista cuyo índice es el valor del contador, con piso, luego incremente el contador. –

6

Puede utilizar islice de itertools:

from itertools import islice 

def downsample_to_proportion(rows, proportion=1): 
    return list(islice(rows, 0, len(rows), int(1/proportion))) 

Uso:

x = range(1,10) 
print downsample_to_proportion(x, 0.3) 
# [1, 4, 7] 
3

En lugar de islice() + list() es más eficaz utilizar la sintaxis rebanada directamente si la entrada es ya un tipo de secuencia:

def downsample_to_proportion(rows, proportion): 
    return rows[::int(1/proportion)] 
0

Esta solución puede ser un poco exagerada para el póster original, pero pensé que compartiría el código que he estado usando para resolver este y otros problemas similares.

Es un poco largo (unas 90 líneas), pero si a menudo tiene esta necesidad, quiere un oneliner fácil de usar y necesita un entorno libre de python puro, entonces creo que podría ser útil.

Básicamente, lo único que tiene que hacer es pasar su lista a la función y decirle qué longitud que usted quiere que su nueva lista que sea, y la función, o bien:

  • reducir el tamaño de su lista dejando caer los artículos si la nueva longitud es más pequeña, al igual que las respuestas anteriores ya sugeridas.
  • tramo/exclusiva su lista (lo contrario de reducción) si la nueva longitud es mayor, con la opción añadida de que usted puede decidir si desea:
    • interpolar linealmente BW los valores conocidos (elige automáticamente si la lista contiene enteros o flotadores)
    • duplican cada valor para que ocupen un tamaño proporcional de la nueva lista (elegidos automáticamente si la lista contiene los no números)
    • tire de los valores originales de separación y dejar huecos entre

Todo se recoge dentro de una función, por lo que si lo necesita solo copie y péguelo en su secuencia de comandos y puede comenzar a usarlo de inmediato.

Por ejemplo, usted podría decir:

origlist = [0,None,None,30,None,50,60,70,None,None,100] 
resizedlist = ResizeList(testlist, 21) 
print(resizedlist) 

y obtener

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

Nota que las inexactitudes menores ocurrirán debido a las limitaciones de punto flotante. Además, escribí esto para Python 2.x, así que para usarlo en Python 3.x simplemente agregue una sola línea que diga xrange = range.

Y aquí hay un ingenioso truco para interpolar entre los subelementos posicionados en una lista de listas. Así, por ejemplo, puede interpolar fácilmente entre tuplas de color RGB para crear un degradado de color de xn de pasos. Suponiendo una lista de tuplas de color RGB de 3 y una variable GRADIENTLENGTH deseado hacer esto con:

crosssections = zip(*rgbtuples) 
grad_crosssections = (ResizeList(spectrum,GRADIENTLENGTH) for spectrum in crosssections) 
rgb_gradient = [list(each) for each in zip(*grad_crosssections)] 

Probablemente podría necesitar un buen número de optimizaciones, que tenía que hacer un poco de experimentación. Si crees que puedes mejorar, puedes editar mi publicación. Aquí está el código:

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