Pergunta

Eu tenho uma lista de Python com várias entradas, que eu preciso descer a amostra usando:

  • Um número máximo de linhas. Por exemplo, limitando uma lista de 1234 entradas a 1000.
  • Uma proporção das linhas originais. Por exemplo, tornando a lista 1/3 seu comprimento original.

(Eu preciso ser capaz de fazer os dois lados, mas apenas um é usado de cada vez).

Acredito que, para o número máximo de linhas, posso apenas calcular a proporção necessária e passar isso para o downsizer proporcional:

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

... Então, eu realmente só preciso de uma função de downsampling. Alguma dica, por favor?

EDITAR: A lista contém objetos, não valores numéricos, então não preciso interpolar. A queda de objetos está bem.

SOLUÇÃO:

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

Obrigado.

Foi útil?

Solução

Mantenha um contador, que você incrementa pelo segundo valor. Piso de cada vez e produza o valor nesse índice.

Outras dicas

Você pode usar islice a partir 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]

Ao invés de islice() + list() É mais eficiente usar a sintaxe da fatia diretamente se a entrada já for do tipo de sequência:

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

Essa solução pode ser um pouco exagerada para o pôster original, mas pensei em compartilhar o código que tenho usado para resolver isso e problemas semelhantes.

É um pouco demorado (cerca de 90 linhas), mas se você costuma ter essa necessidade, deseja um Oneliner fácil de usar e precisa de um ambiente livre de dependência python puro, acho que pode ser útil.

Basicamente, a única coisa que você precisa fazer é passar sua lista para a função e dizer qual comprimento você deseja que sua nova lista seja, e a função também:

  • redução do tamanho Sua lista soltando itens se o novo comprimento for menor, assim como as respostas anteriores já sugeridas.
  • esticar/Upscale sua lista (o oposto de reduzir o tamanho) se o novo comprimento for maior, com a opção adicional que você pode decidir se deve:
    • Interpolar linearmente BW Os valores conhecidos (escolhidos automaticamente se a lista contém ints ou flutuadores)
    • duplique cada valor para que ocupem um tamanho proporcional da nova lista (escolhida automaticamente se a lista contiver não-numbers)
    • separe os valores originais e deixe lacunas entre

Tudo é coletado dentro de uma função; portanto, se você precisar, basta copiar e colá -lo no seu script e você pode começar a usá -lo imediatamente.

Por exemplo, você pode dizer:

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

e pegue

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

Observe que pequenas imprecisões ocorrerão devido a limitações de ponto flutuante. Além disso, escrevi isso para o Python 2.x, por assim dizer, para usá -lo no python 3.x, basta adicionar uma única linha que diz xrange = range.

E aqui está um truque bacana para interpolar entre subitems posicionados em uma lista de listas. Por exemplo, você pode interpolar facilmente entre tuplas de cor RGB para criar um gradiente de cores de x nr de etapas. Assumindo uma lista de tuplas de cor RGB de 3 e uma variável de comprimento de gradiente desejada com que você faz isso:

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

Provavelmente poderia precisar de algumas otimizações, eu tive que fazer bastante experimentação. Se você sentir que pode melhorar, sinta -se à vontade para editar minha postagem. Aqui está o 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

Não pode resolver o seu problema? Mais exemplos estão disponíveis aqui

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top