Уменьшение количества записей в списке (без интерполяции)
У меня есть список Python с количеством записей, которые мне нужно уменьшить, используя либо:
- Максимальное количество строк. Например, ограничение списка из 1234 записей до 1000.
- Пропорция оригинальных строк. Например, сделать список 1/3 его первоначальной длины.
(Я должен быть в состоянии сделать оба пути, но только один используется одновременно).
Я считаю, что для максимального количества строк я могу просто рассчитать необходимую пропорцию и передать ее пропорциональному уменьшителю:
def downsample_to_max(self, rows, max_rows):
return downsample_to_proportion(rows, max_rows / float(len(rows)))
... так что мне действительно нужна только одна функция понижающей дискретизации. Любые намеки, пожалуйста?
РЕДАКТИРОВАТЬ: список содержит объекты, а не числовые значения, поэтому мне не нужно интерполировать. Сбрасывать предметы в порядке.
РЕШЕНИЕ:
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
Благодарю.
6 ответов
Держите счетчик, который вы увеличиваете на второе значение. Этаж каждый раз, и выведите значение по этому индексу.
Ты можешь использовать islice
от itertools
:
from itertools import islice
def downsample_to_proportion(rows, proportion=1):
return list(islice(rows, 0, len(rows), int(1/proportion)))
Использование:
x = range(1,10)
print downsample_to_proportion(x, 0.3)
# [1, 4, 7]
Вместо islice()
+ list()
более эффективно использовать синтаксис слайса напрямую, если входные данные уже являются типом последовательности:
def downsample_to_proportion(rows, proportion):
return rows[::int(1 / proportion)]
Это решение может быть немного излишним для оригинального плаката, но я подумал, что поделюсь кодом, который я использовал для решения этой и подобных проблем.
Это немного длинно (около 90 строк), но если вам часто это нужно, вы хотите простой в использовании oneliner, и вам нужна среда, свободная от чисто Python, то я считаю, что это может быть полезно.
По сути, единственное, что вам нужно сделать, это передать свой список функции и сообщить ему, какой длины вы хотите, чтобы ваш новый список был, и функция будет либо:
- уменьшите свой список, отбросив элементы, если новая длина меньше, так же, как предыдущие предложения, уже предложенные.
- растянуть/ увеличить ваш список (в противоположность уменьшению), если новая длина больше, с добавленной опцией, которую вы можете решить:
- линейная интерполяция между известными значениями (автоматически выбирается, если список содержит целые числа или числа с плавающей запятой)
- дублируйте каждое значение, чтобы они занимали пропорциональный размер нового списка (автоматически выбирается, если список содержит не числа)
- разделить исходные значения и оставить промежутки между
Все собрано внутри одной функции, поэтому, если вам это нужно, просто скопируйте и вставьте его в свой скрипт, и вы сможете сразу начать использовать его.
Например, вы можете сказать:
origlist = [0,None,None,30,None,50,60,70,None,None,100]
resizedlist = ResizeList(testlist, 21)
print(resizedlist)
и получить
[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]
Обратите внимание, что незначительные неточности произойдут из-за ограничений с плавающей запятой. Кроме того, я написал это для Python 2.x, поэтому, чтобы использовать его на Python 3.x, просто добавьте одну строку, которая говорит xrange = range
,
И здесь есть хитрый трюк для интерполяции между позиционированными подпунктами в списке списков. Так, например, вы можете легко интерполировать между кортежами RGB цвета, чтобы создать градиент цвета по x nr шагов. Предполагая список кортежей RGB из 3 и желаемую переменную GRADIENTLENGTH, вы делаете это с помощью:
crosssections = zip(*rgbtuples)
grad_crosssections = ( ResizeList(spectrum,GRADIENTLENGTH) for spectrum in crosssections )
rgb_gradient = [list(each) for each in zip(*grad_crosssections)]
Наверное, может потребоваться немало оптимизаций, мне пришлось немного поэкспериментировать. Если вы чувствуете, что можете улучшить его, не стесняйтесь редактировать мой пост. Вот код:
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
Не удается решить вашу проблему с помощью random.choices()? Дополнительные примеры доступны здесь
Со ссылкой на ответ Игнасио Васкес-Абрамса:
Выведите 3 числа из 7 доступных:
msg_cache = [1, 2, 3, 4, 5, 6]
msg_n = 3
inc = len(msg_cache) / msg_n
inc_total = 0
for _ in range(0, msg_n):
msg_downsampled = msg_cache[math.floor(inc_total)]
print(msg_downsampled)
inc_total += inc
Выход:
0
2
4
Полезно для понижения дискретизации многих сообщений журнала до меньшего подмножества.