Преобразование данных временной метки фронта прямоугольной волны в данные, подходящие для построения графиков

Это пример данных, с которыми я работаю:

введите здесь описание изображения

Это описание булевой прямоугольной волны со следующими характеристиками:

  • Числа в столбце «Вниз» представляют собой случайные целые числа, но всегда должны отображаться как ноль.
  • Числа в столбце «Вверх» также являются случайными целыми числами, но всегда должны отображаться как 1.
  • В большинстве случаев «Вверх» и «Вниз» регулярно чередуются, но время от времени могут присутствовать дополнительные «Вниз» или «Вверх».
  • задержка между переходами нерегулярная - значения в столбце «Время» следует считать случайными, но последовательными (отсортированными).

Вот изображение боке, которое мне нужно:  введите описание изображения здесь

Требуется просмотр как исходных точек (включая повторяющиеся, например, как t = 1,2), так и наложения линии.

Насколько я понимаю, самый простой способ - интерполировать одну точку между каждой парой, причем значение y является значением Up / Down для предыдущей точки, а значение x - для следующей точки. Затем в режиме боке постройте исходные данные в виде разброса и данные + InterpolatedData в виде линейного графика. Это означает два разных символа в сюжете, которых я бы предпочел избежать (чтобы иметь возможность использовать новую легенду .click_policy = "hide"), но это допустимо.

Интерполяция легко достигается с помощью обычного кода Python, но я бы предпочел найти способ сделать это с помощью панд для единообразия кода. Подходит ли pandas для такой интерполяции?

Оптимальное решение привело бы к единственному глифу в боке. Я готов пожертвовать производительностью ради этого. Хорошее решение заменяет мой чистый код Python кодом pandas.


person jmr    schedule 14.06.2017    source источник


Ответы (3)


Ниже приведен полный сценарий, который работает для Боке.

import pandas as pd

from bokeh.io import output_file, show
from bokeh.plotting import figure

output_file("signal.html")

data = pd.DataFrame(dict(
    time=[1, 1.1, 1.2, 1.5, 1.8], 
    down=[19371, None, None, 38175, None],
    up=[None, 36823, 91046, None, 47722]
))

data['mapped'] = data.up.isnull()

# This computes the "step" data
x, y = [], []
prev = -1
for index, row in data.iterrows():
    if row.mapped != prev and prev>=0:
        x.append(row.time)
        y.append(prev)
    x.append(row.time)
    y.append(int(row.mapped))
    prev = int(row.mapped)

p = figure()
p.line(x=x, y=y, legend="signal")
p.circle(x=data.time, y=data.mapped, legend="signal")

p.legend.click_policy="hide"

show(p)

Это дает следующий график боке с интерактивной легендой:

введите здесь описание изображения

person bigreddot    schedule 15.06.2017
comment
Отличный ответ, у которого есть чему поучиться как на Pandas, так и на Bokeh, в частности, что можно объединить несколько глифов, дав им одно и то же название легенды. Спасибо! Однако ваш ответ имеет большой недостаток - он значительно медленнее, чем мой текущий код: около 2,4 с в моем тестовом файле на 7700 баллов по сравнению с примерно 1,4 с для моего. Я подозреваю, что 7700 вызовов boolean_map () - это то, что вредит вам. Я отправлю свой код в качестве ответа, если он будет интересен. - person jmr; 15.06.2017
comment
вполне возможно, размер данных не был указан, поэтому я остановился на самом простом. Я обновил ответ, чтобы он был более эффективным. - person bigreddot; 15.06.2017
comment
Это должно быть down.isnull () или up.notnull (), но по причине, которую я не могу объяснить, это все равно занимает те же 2,4 секунды. Ну что ж. - person jmr; 15.06.2017

Это должно вас начать. Обратите внимание, что matplotlib использовался напрямую, а не pandas (который фактически использует matplotlib) для построения.

import pandas as pd
import matplotlib.pyplot as plt

Сопоставьте данные с пандами:

data = pd.read_csv('wave_data.csv', sep=';')

def boolean_map(row):
   if pd.notnull(row.Down):
      return 0
   else:
      return 1

data['BooleanMapped'] = data.apply(boolean_map, axis=1)

Вывод

Постройте данные:

x = list(data['Time'])
y = list(data['BooleanMapped'])
plt.ylim(-0.1, 1.5)
plt.xlim(0.9, 2)
plt.step(x, y, where='post')
plt.show()

Пример графика

person tvgriek    schedule 14.06.2017
comment
Вопрос касался того, как создать интерактивный сюжет с использованием Bokeh, а не Matplotlib. - person bigreddot; 15.06.2017
comment
Спасибо. Интересно видеть, что у matplotlib есть API для этого точного использования. -кейс. Тем не менее, я уже довольно привязан к графикам Bokeh, так что мое решение на чистом Python пока придется делать, тем более что бенчмаркинг доказал его приемлемость. - person jmr; 15.06.2017

Как было объявлено в комментарии, вот код, который я сейчас использую:

    import timeit
    start_time = timeit.default_timer()

    import io
    import os
    import sys
    import math

    import pandas

    from bokeh.io import output_file, show
    from bokeh.plotting import figure

    output_file("signal.html")

    def fillSquareWave(xi, downi, upi):
        resultx = [0,]
        resulty = [0,]
        lasty = 0
        for x, down, up in zip(xi, downi, upi):
            if (math.isnan(down)) and (math.isnan(up)):
                # no dropna() at pandas level, since it'd drop if EITHER is a nan (we only drop if BOTH are)
                pass
            else:
                # append extra point
                resultx.append(x)
                resulty.append(lasty)

                # append real, current point
                resultx.append(x)
                if (not math.isnan(down)) and (math.isnan(up)):
                    resulty.append(0)
                    lasty = 0
                elif (math.isnan(down)) and (not math.isnan(up)):
                    resulty.append(1)
                    lasty = 1
                else:
                    print("x: ", x, " down: ", down, " up: ", up)
                    assert(False)
        return resultx, resulty

    sourcefile = os.path.basename(sys.argv[1])
    bothpts = pandas.read_csv(sourcefile, usecols=['Time', 'down', 'up'])
    uppts = pandas.read_csv(sourcefile, usecols=['Time', 'up']) 
    uppts.dropna(inplace=True)
    uppts['up'] = 1
    downpts = pandas.read_csv(sourcefile, usecols=['Time', 'down']) 
    downpts.dropna(inplace=True)
    downpts['down'] = 0

    php = figure()
    php.scatter(uppts['Time'], uppts['up'], legend='up',)
    php.scatter(downpts['Time'], downpts['down'], legend='down')
    xdata, ydata = fillSquareWave(bothpts['Time'], bothpts['down'], bothpts['up'])
    php.line(xdata, ydata, legend='overlay')
    php.legend.click_policy = "hide"

    show(php)

    print("Rendered in %.2fs" % (timeit.default_timer() - start_time))

Этот код создает график, который немного отличается от вопроса - он начинает рисование в точке (0,0), а не в координатах первой точки в файле CSV.

Изменить: можно удалить несколько вызовов read_csv () с помощью следующего кода, но время выполнения в основном не изменится.

    bothpts = pandas.read_csv(sourcefile, usecols=['Time', 'down', 'up'])
    uppts = copy(bothpts)
    del uppts['down']
    uppts.dropna(inplace=True)
    uppts['up'] = 1
    downpts = copy(bothpts)
    del downpts['up']
    downpts.dropna(inplace=True)
    downpts['down'] = 0
person jmr    schedule 15.06.2017