Байесовы блоки

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
Всем добрый день!
Надеюсь, тут есть те, кто работал с байесовыми блоками. Проблема у меня в том, что они неправильно рисуются для моих данных. При том, что для тестовых данных все ок. Может, кто-то подскажет, как сделать, чтобы и для моих рисовалось?.. Очень, очень надеюсь на ответ; код и данные ниже.

Python:
import numpy as np
from scipy import stats
import pylab as pl


def test_dist():
    np.random.seed(0)
    x = np.concatenate([stats.cauchy(-5, 1.8).rvs(500),
                        stats.cauchy(-4, 0.8).rvs(2000),
                        stats.cauchy(-1, 0.3).rvs(500),
                        stats.cauchy(2, 0.8).rvs(1000),
                        stats.cauchy(4, 1.5).rvs(500)]) # первое число - центр распределения, второе - разброс, число rvs  - число случайных значений

    x = x[(x > -15) & (x < 15)]
    return x


def create_data(name):
    x = []
    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            x.append(float(data_list[2]) - float(data_list[3]) + float(data_list[4]))

    # print(x)
    return x


def plot_blocks(t):
    pl.hist(t, bins=200, histtype='stepfilled', alpha=0.2, density=True)
    pl.hist(t, bins=bayesian_blocks(t), color='black', histtype='step', density=True)

    return pl.show()


def bayesian_blocks(t):
    """Bayesian Blocks Implementation

    By Jake Vanderplas.  License: BSD
    Based on algorithm outlined in http://adsabs.harvard.edu/abs/2012arXiv1207.5578S

    Parameters
    ----------
    t : ndarray, length N
        data to be histogrammed

    Returns
    -------
    bins : ndarray
        array containing the (N+1) bin edges

    Notes
    -----
    This is an incomplete implementation: it may fail for some
    datasets.  Alternate fitness functions and prior forms can
    be found in the paper listed above.
    """
    # copy and sort the array
    t = np.sort(t)
    N = t.size

    # create length-(N + 1) array of cell edges
    edges = np.concatenate([t[:1],
                            0.5 * (t[1:] + t[:-1]),
                            t[-1:]])
    block_length = t[-1] - edges
    # arrays needed for the iteration
    nn_vec = np.ones(N)
    best = np.zeros(N, dtype=float)
    last = np.zeros(N, dtype=int)

    # -----------------------------------------------------------------
    # Start with first data cell; add one cell at each iteration
    # -----------------------------------------------------------------
    for K in range(N):
        # Compute the width and count of the final bin for all possible
        # locations of the K^th changepoint
        width = block_length[:K + 1] - block_length[K + 1]
        count_vec = np.cumsum(nn_vec[:K + 1][::-1])[::-1]

        # evaluate fitness function for these possibilities
        fit_vec = count_vec * (np.log(count_vec / width) - 1)
        fit_vec -= 4  # 4 comes from the prior on the number of changepoints
        fit_vec[1:] += best[:K]

        # find the max of the fitness: this is the K^th changepoint
        i_max = np.argmax(fit_vec)
        last[K] = i_max
        best[K] = fit_vec[i_max]

    # -----------------------------------------------------------------
    # Recover changepoints by iteratively peeling off the last block
    # -----------------------------------------------------------------
    change_points = np.zeros(N, dtype=int)
    i_cp = N
    ind = N
    while True:
        i_cp -= 1
        change_points[i_cp] = ind
        if ind == 0:
            break
        ind = last[ind - 1]
    change_points = change_points[i_cp:]
    # print(change_points)

    return edges[change_points]


def create_and_show():
    # narr = test_dist() #тестовые данные для просмотра разбиения
    narr = create_data('data_n8.txt')
    plot_blocks(narr)


if __name__ == '__main__':
    create_and_show()
 

Вложения

  • data_n8.txt
    32,1 КБ · Просмотры: 1

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
ваш код ошибку выдает, выход за предел массива, это и есть неправильное рисование?
 

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
ваш код ошибку выдает, выход за предел массива, это и есть неправильное рисование?
Нет, должна быть нарисована ступенчатая гистограмма, как если взять тестовый код. Там ступенчатая гистограмма закрашенная должна быть и поверх нее - прозрачная. В общем, примерно как тут:
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
Нет, должна быть нарисована ступенчатая гистограмма, как если взять тестовый код. Там ступенчатая гистограмма закрашенная должна быть и поверх нее - прозрачная. В общем, примерно как тут:
да, у вас в файле первая строка просто пустая

с байесовыми блоками я не работал
но у вас как минимум две ошибки
1. Вы в функцию bayesian_blocks передаете простой list, а не ndarray, как требуют, в тестовых данных у вас ndarray
2. С вашими данными у вас предупреждения рантайма деления на ноль, и потом предупреждение гистограмм в numpy
Код:
RuntimeWarning: divide by zero encountered in true_divide
  fit_vec = count_vec * (np.log(count_vec / width) - 1)
C:\Python310\lib\site-packages\numpy\lib\histograms.py:906: RuntimeWarning: invalid value encountered in true_divide
  return n/db/n.sum(), bin_edges
с тестовыми данными таких предупреждений нет

думаю, нужно смотреть в этом направлении
смотреть, в чем отличия тестовых данных и ваших, и устранить предупреждения рантайма
 
Последнее редактирование:

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
да, у вас в файле первая строка просто пустая

с байесовыми блоками я не работал
но у вас как минимум две ошибки
1. Вы в функцию bayesian_blocks передаете простой list, а не ndarray, как требуют, в тестовых данных у вас ndarray
2. С вашими данными у вас предупреждения рантайма деления на ноль, и потом предупреждение гистограмм в numpy
Код:
RuntimeWarning: divide by zero encountered in true_divide
  fit_vec = count_vec * (np.log(count_vec / width) - 1)
C:\Python310\lib\site-packages\numpy\lib\histograms.py:906: RuntimeWarning: invalid value encountered in true_divide
  return n/db/n.sum(), bin_edges
с тестовыми данными таких предупреждений нет

думаю, нужно смотреть в этом направлении
смотреть, в чем отличия тестовых данных и ваших, и устранить предупреждения рантайма
Спасибо! У меня это очень узкоспециализированная задача, так что я сразу понимала, что мало шансов, что мне скажут решение. Ошибки устраняю
 
  • Мне нравится
Реакции: regnor

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8

regnor, не могли бы Вы помочь мне еще раз? У меня 3 колонки значений:

-1.0 -0.5 5
-0.5 0.0 40
0.0 0.5 20
0.5 1.0 5
Мне нужно, чтоб элементы гистограммы рисовались вот так:
от -1.0 до -0.5
от -0.5 до 0.0 и т.д.
Сейчас я смогла сделать так:
Python:
def create_and_visualise(name):
    x = []
    t = []
    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            x.append(float(data_list[2]))
            t.append(float(data_list[0]))

    pl.hist(t, weights=x, bins=20, histtype='stepfilled', alpha=0.2, density=True)
    pl.hist(t, weights=x, bins=bayesian_blocks(t, x), color='black', histtype='step', density=True)
    pl.show()

    return np.array(t), np.array(x)
Но там не учитывается вторая колонка (которая в коде должна иметь номер 1, т.к. первая - 0).
 

Вложения

  • data_test.txt
    54 байт · Просмотры: 2

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
если правильно понял вопрос, то вам нужно как то так (в bins указать t)
закомментировал второй hist, видимо вы поменяли функцию bayesian_blocks и в ней стало два параметра
Python:
def create_and_visualise(name):
    x = []
    t = []
    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            x.append(float(data_list[2]))
            t.append(float(data_list[0]))

    pl.hist(t, weights=x, bins=t, histtype='stepfilled', alpha=0.2, density=True)
    # pl.hist(t, weights=x, bins=bayesian_blocks(t, x), color='black', histtype='step', density=True)
    pl.show()

    return np.array(t), np.array(x)

но в документации написано, что все бины, кроме последнего, с открытой правой границей, то есть они располагаются не равномерно, хотя я этого не увидел
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html - второй параметр
 
Последнее редактирование:
  • Мне нравится
Реакции: Наги

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
если правильно понял вопрос, то вам нужно как то так (в bins указать t)
закомментировал второй hist, видимо вы поменяли функцию bayesian_blocks и в ней стало два параметра
Python:
def create_and_visualise(name):
    x = []
    t = []
    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            x.append(float(data_list[2]))
            t.append(float(data_list[0]))

    pl.hist(t, weights=x, bins=t, histtype='stepfilled', alpha=0.2, density=True)
    # pl.hist(t, weights=x, bins=bayesian_blocks(t, x), color='black', histtype='step', density=True)
    pl.show()

    return np.array(t), np.array(x)

но в документации написано, что все бины, кроме последнего, с открытой правой границей, то есть они располагаются не равномерно, хотя я этого не увидел
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html - второй параметр
Да, спасибо! Вроде, оно. А почему, если например подставить х а не t: pl.hist(t, weights=x, bins=x, histtype='stepfilled', alpha=0.2, density=True) будет выдавать ошибку?
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
Да, спасибо! Вроде, оно. А почему, если например подставить х а не t: pl.hist(t, weights=x, bins=x, histtype='stepfilled', alpha=0.2, density=True) будет выдавать ошибку?
потому что в bins при указании массива значения должны увеличиваться и быть в диапазоне массива расчета (в вашем случае массив t)
если в файле вы поменяете третий столбец на 5 10 20 30, то ошибки не будет, будет предупреждение рантайма, потому что эти значения не попадают в диапазон t
и в вашем случае массив x вы используете для определения весов (weights=x), если его указать и в bins, будет путаница
возможно, вам нужно второй столбец определить в bins
 

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
потому что в bins при указании массива значения должны увеличиваться и быть в диапазоне массива расчета (в вашем случае массив t)
если в файле вы поменяете третий столбец на 5 10 20 30, то ошибки не будет, будет предупреждение рантайма, потому что эти значения не попадают в диапазон t
и в вашем случае массив x вы используете для определения весов (weights=x), если его указать и в bins, будет путаница
возможно, вам нужно второй столбец определить в bins
Спасибо, я поняла!
 
Последнее редактирование:

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
потому что в bins при указании массива значения должны увеличиваться и быть в диапазоне массива расчета (в вашем случае массив t)
если в файле вы поменяете третий столбец на 5 10 20 30, то ошибки не будет, будет предупреждение рантайма, потому что эти значения не попадают в диапазон t
и в вашем случае массив x вы используете для определения весов (weights=x), если его указать и в bins, будет путаница
возможно, вам нужно второй столбец определить в bins
Еще раз хотела бы попросить помощи. Я все мучаюсь с этим кодом. В общем мне надо без изменения данных нарисовать правильный рисунок, то есть чтоб по вертикальной оси он шел от 0, а не от 5, чтобы справа рисунок был тоже от 0 шел по вертикали и была горизонтальная линия, соответствующая 5, потому что сейчас ее почему-то нет. То есть в соответствие с данными рисунок должен быть слева и справа симметричен. Что касается середины данных - то там именно оранжевая линия должна показывать усредненные значения. То есть она сейчас на 40 и 20, а надо чтоб линия была ровной, а не со ступенькой, как сейчас, на (40 + 20) = 30. Пожалуйста, не оставьте в беде Т.Т
Да, и как я поняла, надо работать только в функции create_and_visualise(). Функция bayesian_blocks() закончена и правильна. То есть, у меня работает правильно, теперь только не так визуализируется.
Python:
import numpy as np
import pylab as pl


def create_and_visualise(name):
    x = []
    t1 = []
    t2 = []
    tbins = []

    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            x.append(float(data_list[2]))
            t1.append(float(data_list[0]))
            t2.append(float(data_list[1]))
            tbins.append(float(data_list[0]))

    tbins.append(t2[-1])

    edges, change_points = bayesian_blocks(t1, np.array(tbins), x)
    print(f'{edges=}')
    print(f'{change_points=}')

    pl.step(t1, x, where='post')
    pl.step(edges, x, where='post')
    pl.show()


def bayesian_blocks(t, tb, x):
    # copy and sort the array
    t = np.sort(t)
    N = t.size

    # create length-(N + 1) array of cell edges
    edges = tb
    block_length = t[-1] - edges

    # arrays needed for the iteration
    best = np.zeros(N, dtype=float)
    last = np.zeros(N, dtype=int)

    # -----------------------------------------------------------------
    # Start with first data cell; add one cell at each iteration
    # -----------------------------------------------------------------
    for K in range(N):
        # Compute the width and count of the final bin for all possible
        # locations of the K^th changepoint
        width = block_length[:K + 1] - block_length[K + 1]
        count_vec = np.cumsum(x[:K + 1][::-1])[::-1]

        # evaluate fitness function for these possibilities
        fit_vec = count_vec * (np.log(count_vec / width) - 1)
        fit_vec -= 4  # 4 comes from the prior on the number of changepoints
        fit_vec[1:] += best[:K]

        # find the max of the fitness: this is the K^th changepoint
        i_max = np.argmax(fit_vec)
        last[K] = i_max
        best[K] = fit_vec[i_max]

    # -----------------------------------------------------------------
    # Recover changepoints by iteratively peeling off the last block
    # -----------------------------------------------------------------
    change_points = np.zeros(N, dtype=int)
    i_cp = N
    ind = N
    while True:
        i_cp -= 1
        change_points[i_cp] = ind
        if ind == 0:
            break
        ind = last[ind - 1]
    change_points = change_points[i_cp:]

    return edges[change_points], change_points


def create_and_show():
    create_and_visualise('data_test.txt')


if __name__ == '__main__':
    create_and_show()
 

Вложения

  • data_test.txt
    53 байт · Просмотры: 1

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
ну я сомневаюсь, что проблема в отрисовке, так как вы там никаких вычислений не делаете, просто рисуете и все

В общем мне надо без изменения данных нарисовать правильный рисунок, то есть чтоб по вертикальной оси он шел от 0, а не от 5
у вас в х массив [5.0, 40.0, 20.0, 5.0], поэтому график от 5

чтобы справа рисунок был тоже от 0 шел по вертикали и была горизонтальная линия, соответствующая 5, потому что сейчас ее почему-то нет
тут не понял

То есть она сейчас на 40 и 20, а надо чтоб линия была ровной, а не со ступенькой, как сейчас, на (40 + 20) = 30. Пожалуйста, не оставьте в беде Т.Т
ну опять же, это из за того что в х массив [5.0, 40.0, 20.0, 5.0]


у вас график строиться относительно массива х, он у вас не меняеться...

в вычислениях разбираться не стал, простите
 

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
Справа от 0.5 до 1.0 должна быть линия (синия), но ее нет. Хотя должна быть. Ее бы как-нибудь добавить..

ну опять же, это из за того что в х массив [5.0, 40.0, 20.0, 5.0]
Да, я понимаю. Но мне бы как-то усреднить значения 20 и 40, чтоб они вместе были 30 и на графике так рисовалось

в вычислениях разбираться не стал, простите
Там и не надо, это точно
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 668
475
83
Да, я понимаю. Но мне бы как-то усреднить значения 20 и 40, чтоб они вместе были 30 и на графике так рисовалось
ну посчитайте среднее значение
возможно, я не понимаю в чем проблема, но мне кажеться проблема, в вычислениях...
Там и не надо, это точно
почему вы так уверены?
 

Наги

Пользователь
Пользователь
Окт 25, 2020
74
5
8
ну посчитайте среднее значение
возможно, я не понимаю в чем проблема, но мне кажеться проблема, в вычислениях...
Да, у меня, вроде, это получилось. Вот так дополнила функцию:
Python:
def create_and_visualise(name):
    x = []
    t1 = []
    t2 = []
    tbins = []
    xbins = []

    with open(name, 'r') as f:
        for line in f:
            data_list = line.split()

            t1.append(float(data_list[0]))
            t2.append(float(data_list[1]))
            x.append(float(data_list[2]))
            tbins.append(float(data_list[0]))

    tbins.append(t2[-1])

    vals = 0
    count = 0
    for ind, val in enumerate(x):
        if ind == 0 or ind == len(x) - 1:
            pass
        else:
            vals += val
            count += 1
    div = vals / count

    for ind, val in enumerate(x):
        if ind == 0 or ind == len(x) - 1:
            xbins.append(val)
        else:
            xbins.append(div)

    edges, change_points = bayesian_blocks(t1, np.array(tbins), x)
    print(f'{edges=}')
    print(f'{change_points=}')

    pl.step(t1, x, where='post')
    pl.step(edges, xbins, where='post')
    pl.show()

почему вы так уверены?
Мне так научник сказал..

Но все-таки.. как мне добавить точку на график? Тм не хватает точки
Справа от 0.5 до 1.0 должна быть линия (синия), но ее нет. Хотя должна быть. Ее бы как-нибудь добавить..
 

4olshoy_blen

Популярный
Пользователь
Ноя 13, 2022
435
119
43
Но все-таки.. как мне добавить точку на график? Тм не хватает точки
Не лучше ли все эти вопросы перенаправить вашему научнику, нежели тут ждать помощи по специфической теме?
 

Форум IT Специалистов