Сжатие синусоидальной таблицы - PullRequest
5 голосов
/ 06 августа 2011

У меня есть большой массив с 1024 записями, которые имеют 7-битные значения в range(14, 86)

Это означает, что существует несколько диапазонов индексов, которые имеют одно и то же значение.

Например,

consider the index range 741 to 795. It maps to 14
consider the index range 721 to 740. It maps to 15
consider the index range 796 to 815. It maps to 15

Я хочу передать эту карту в программу на python, которая выдаст следующее:

if((index >= 741) and (index <= 795)) return 14;
if((index >= 721) and (index <= 740)) return 15;
if((index >= 796) and (index <= 815)) return 15;

Некоторый код для groupby сопоставленного значения готов, но у меня возникают трудности с кодированиемсоставить выражение, используя pairwise.

Кто-нибудь делал что-то подобное раньше?

Я загрузил набор данных в двух формах:

Обычный, , упорядоченный по индексу.

Группируется по отображаемому значению .

Ответы [ 2 ]

3 голосов
/ 06 августа 2011

Если вы не возражаете, слегка различные значения из-за округления, я могу сжать это действительно хорошо для вас.

from math import pi, sin
interval=2*pi/1024
sinval=lambda i:int(round(sin(i*interval)*36))+50

Вот код дляна самом деле делай что хочешь;он работает с

vals = sorted((sinval(i), i) for i in range(1024))

в качестве тестовых данных.Вам нужно изменить порядок val и index в цикле for, если у вас есть индексы в первом столбце.

ranges, oldval, oldidx = [[0, 0]], 0, 0
for val, index in vals:
    if not (val == oldval and index == oldidx + 1):
        ranges[-1].append(oldidx)
        ranges.append([val, index])
    oldval, oldidx = val, index
ranges[-1].append(oldidx)
ranges.pop(0)
ifs = ('if((index >= {1}) and (index <= {2})) return {0};\n'.format(val, start, end)
            for val, start, end in ranges)
print ''.join(ifs)

Редактировать: Упс, я пропустиллиния.Исправлена.Кроме того, ваш множитель был на самом деле 36, а не 35, я должен был округлить (14, 86) до (15, 85) в моей голове.

Редактировать 2: чтобы показать вам, как хранить только четвертьтаблица.

from math import pi, sin

full = 1024
half = 512
quarter = 256
mag = 72
offset = 50

interval = 2 * pi / full

def sinval(i):
    return int(round(sin(i * interval) * (mag // 2))) + offset

vals = [sinval(i) for i in range(quarter)]

def sintable(i):
    if  i >= half + quarter:
        return 2 * offset - vals[full - i - 1]
    elif  i >= half:
        return 2 * offset - vals[i - half]
    elif i >= quarter:
        return vals[half - i - 1]
    else:
        return vals[i]

for i in range(full):
    assert -1 <= sinval(i) - sintable(i) <= 1

Если вычесть смещение из таблицы, просто сделайте первые два -vals[...].

Кроме того, сравнение внизу нечеткое, потому что я получаю 72ошибки за это.Это просто потому, что ваши значения округлены до целых чисел;это все места, где вы находитесь на полпути между двумя значениями, поэтому точность уменьшается очень незначительно.

2 голосов
/ 06 августа 2011

После закрытия я запоздало нашел это решение «Какой самый Pythonic способ идентифицировать последовательные дубликаты в списке?» .


Примечание: с периодической функцией fn, такой как sine , вы можете обойтись, только сохранив четверть (то есть 256 значений) или половину таблицы, а затем выполнить немного (исправлено)точка) арифметика по индексу во время поиска.Как я уже говорил, если вы не сохраните смещение +50, вам потребуется на один бит меньше, за счет одного целочисленного сложения после времени поиска.Следовательно, сжатие 79% легко достижимо.RLE даст вам больше.Даже если у fn есть шум, вы можете получить приличное сжатие с помощью этого общего подхода.

Как указывал agf, ваш f(n) = 50 + 36*sin(72*pi*n/1024) = 50 + g(n), скажем.

Итак, запишите в таблицу 256значения g(n) = 36*sin(72*pi*n/1024), только для диапазона n = 0..255

Тогда f (n) легко вычисляется по формуле:

if 0 <= n < 256, f(n) = 50 + g(n)
if 256 <= n < 512, f(n) = 50 + g(511-n)
if 512 <= n < 768, f(n) = 50 - g(n-512)
if 768 <= n < 1024, f(n) = 50 - g(1023-n)

В любом случае, вот обычный компрессор таблицрешение, которое сгенерирует (istart, iend, value) втрое.

Я выбил голову, как сделать это более Pythonical, используя списки и itertools.takewhile ();нуждается в полировке.

#import itertools

table_="""
    0       50
    1       50
    ...
    1021    49
    1022    50
    1023    50""".split()

# Convert values to int. Throw away the indices - will recover them with enumerate()
table = [int(x) for x in table_[1::2]]

compressed_table = []
istart = 0
for i,v in enumerate(table):
    if v != table[i-1]:
        iend = i-1
        compressed_table.append((istart,iend,table[i-1]))
        istart = i
    else:
        continue # skip identical values
# Slightly ugly: append the last value, when the iterator was exhausted
compressed_table.append((istart,i,table[i]))

(NB. Я начал использовать метод сжатия таблиц до того, как agf изменил свой подход ... пытался получить решение itertools или для обработки списков)

...