1

Я хочу иметь интерактивный сюжет в jupyter (4.0.6) с помощью matplotlib (1.5.1). Дело в том, что статический график создается с помощью функции с четырьмя переменными, две из которых являются константами, две из которых являются аргументами ключевого слова, и я хочу интерактивно изменять аргументы ключевых слов.jupyter notebook: обновить график интерактивно - функция имеет константы и аргументы ключевых слов

Возможно ли это, и если да, то как?

В приведенном ниже концептуальном коде показана функция, которая генерирует график make_figure(...) и команду для создания интерактивного сюжета.

Если я изменить именованные аргументы переменных, то я получаю сообщение об ошибке «взаимодействия() принимает от 0 до 1 позиционных аргументов, но 3 были даны»

концептуальный код:

def make_figure(const_1, const_2, var_1=0.4, var_2=0.8): 
    b = calc_b(var_1, var_2) 
    c = calc_c(b, const_1, const_2) 
    fig, ax = plt.subplots() 
    N, bins, patches = ax.hist(c) 


interact(make_figure, 
     const_1, 
     const_2, 
     var_1=(0.2, 0.4, 0.05), 
     var_2=(0.75, 0.95, 0.05)) 

дополнение 20160325: пример кода

Я пытаюсь создать гистограмму знаков для класса, в зависимости от го е процент, необходимый для достижения 1.0 и 4.0, соответственно.

# setup some marks 
ids_perc = np.random.random(33) 
print("number of entered marks: ", ids_perc.shape) 

основной код для гистограммы; Основная функция: get_marks

# define possible marks 
marks = np.array([1.0, 
        1.3, 
        1.7, 
        2.0, 
        2.3, 
        2.7, 
        3.0, 
        3.3, 
        3.7, 
        4.0, 
        5.0]) 
marks_possible = marks[::-1] 

def get_perc_necessary(min_perc_one, 
         min_perc_four, 
         n_marks): 
    """ 
    calculates an equally spaced array for percentage necessary to get a mark 
    """ 
    delta = (min_perc_one - min_perc_four)/(n_marks-2-1) 
    perc_necessary_raw = np.linspace(start=min_perc_four, 
            stop=min_perc_one, 
            num=n_marks-1) 
    perc_necessary = np.append([0.0], np.round(perc_necessary_raw, decimals=2)) 
    return perc_necessary 


def assign_marks(n_students, 
       perc_necessary, 
       achieved_perc, 
       marks_real): 
    """ 
    get the mark for each student (with a certain achieved percentage) 
    """ 
    final_marks = np.empty(n_students) 

    for cur_i in range(n_students): 
     idx = np.argmax(np.argwhere(perc_necessary <= achieved_perc[cur_i])) 
     final_marks[cur_i] = marks_real[idx] 

    return final_marks 


def get_marks(achieved_perc = ids_perc, 
       marks_real = marks_possible,      
       min_perc_four = 0.15, 
       min_perc_one = 0.85): 

    n_marks = marks.shape[0] 
#  print("n_marks: ", n_marks) 
    n_students = achieved_perc.shape[0] 
#  print("n_students: ", n_students) 

    # ----------------------------- 
    # linear step between each mark 
    perc_necessary = get_perc_necessary(min_perc_one, 
             min_perc_four, 
             n_marks) 

    # test query: there need to be as many percentages as marks 
    if perc_necessary.shape[0] != marks_real.shape[0]: 
     print("the number of marks has to be equal the number of boundaries") 
     raise Exception 

    # ------------ 
    # assign marks 
    final_marks = assign_marks(n_students, 
           perc_necessary, 
           achieved_perc, 
           marks_real)  

    # ------------ 
    # create table 
    fig, ax = plt.subplots() 
    N, bins, patches = ax.hist(final_marks, 
           align='mid', 
           bins=np.append(marks,6.)) # bins=marks 
    ax.xaxis.set_major_formatter(FormatStrFormatter('%0.1f')) 
    bin_centers = 0.5 * np.diff(bins) + bins[:-1] 
    ax.set_xticks(bin_centers) 
    ax.set_xticklabels(marks) 
    ax.set_xlabel("mark") 
    ax.set_ylabel("number of marks") 
    ax.set_ylim(0.0, 6.0) 
    plt.grid(True) 

Теперь, когда я пытаюсь настроить interact делать это

interact(get_marks, 
    min_perc_four=(0.2, 0.4, 0.05), 
    min_perc_one=(0.75, 0.95, 0.05)); 

Я получаю ошибку

ValueError: array([ 0.22366653, 0.74206953, 0.47501716, 0.56536227, 0.54792759, 
    0.60288287, 0.68548973, 0.576935 , 0.84582243, 0.40709693, 
    0.78600622, 0.2692508 , 0.62524819, 0.62204851, 0.5421716 , 
    0.71836192, 0.97194698, 0.4054752 , 0.2185643 , 0.11786751, 
    0.57947848, 0.88659768, 0.38803576, 0.66617254, 0.77663263, 
    0.94364543, 0.23021637, 0.30899724, 0.08695842, 0.50296694, 
    0.8164095 , 0.77892531, 0.5542163 ]) cannot be transformed to a Widget 

Почему смотрит эта ошибка в переменной ids_perc?

ответ

1

Вам необходимо назначить свои переменные явно в interact(). Например, как это:

const_1 = 1 

interact(make_figure, 
     const_1=const_1, 
     const_2=2, 
     var_1=(0.2, 0.4, 0.05), 
     var_2=(0.75, 0.95, 0.05)) 

Или (если это возможно) изменить подпись make_figure, чтобы сделать эти переменные в аргументах ключевых слов, так что вы можете избежать передачи их в явном виде:

def make_figure(const_1=1, const_2=2, var_1=0.4, var_2=0.8): 
    .... 

interact(make_figure, 
     var_1=(0.2, 0.4, 0.05), 
     var_2=(0.75, 0.95, 0.05)) 

Вот MCWE что вы можете попробовать:

def calc_b(v1, v2): 
    return v1 + v2 

def calc_c(v1, v2, v3): 
    return [v1, v2, v3] 

def make_figure(const_1=1, const_2=2, var_1=0.4, var_2=0.8): 
    b = calc_b(var_1, var_2) 
    c = calc_c(b, const_1, const_2) 
    fig, ax = plt.subplots() 
    N, bins, patches = ax.hist(c) 

interact(make_figure, 
     var_1=(0.2, 0.4, 0.05), 
     var_2=(0.75, 0.95, 0.05)); 

Это бежит без ошибок.

На ваш addition 20160325:

Каждый параметр вы передаете для взаимодействия должны быть представима либо из (упрощающей его несколько):

  • слайдера (для tuple с, которые представляют (min, max) и скалярные числа)
  • коробка выбора (для списков строк и словарей)
  • флажок (для булевых)
  • поле ввода (для строк)

Вы передаете (неявно определяя в ваших get_marks два параметров, np.arrays). Итак, interact не знает, как представить, что на слайдере, хэн ошибка.

У вас есть по крайней мере два варианта:

1), чтобы изменить подпись get_marks так, что он принимает параметры, которые interact будет undetstand (см маркированный список выше)

2) сделать еще одну функцию оболочки, будет принимать параметры, которые interact удаляет, но назовет get_marksпосле, преобразуя эти параметры в любые get_marks.

Итак, просто один дополнительный шаг, и все готово. ;-)

UPDATE:

Вот ваш код с оберткой, которая работает для меня. Обратите внимание, что get_marks_interact не нужно принимать все параметры get_marks, и я не передаю списки, так как у interact возникнут проблемы с ними (список должен представлять либо список строк (для виджетов), либо список/набор значений [min, max] значений (для слайдера).

def get_marks(min_perc_four = 0.15, 
       min_perc_one = 0.85, 
       marks=marks_possible, 
       ach_per=ids_perc): 

    marks_real = marks # [0] 
    achieved_perc = ach_per # [0] 

    n_marks = marks_real.shape[0] 
    print("n_marks: ", n_marks) 
    n_students = achieved_perc.shape[0] 
    print("n_students: ", n_students) 

    # ----------------------------- 
    # linear step between each mark 
    perc_necessary = get_perc_necessary(min_perc_one, 
             min_perc_four, 
             n_marks) 

    # test query: there need to be as many percentages as marks 
    if perc_necessary.shape[0] != marks_real.shape[0]: 
     print("the number of marks has to be equal the number of boundaries") 
     raise Exception 

    # ------------ 
    # assign marks 
    final_marks = assign_marks(n_students, 
           perc_necessary, 
           achieved_perc, 
           marks_real) 

    # ------------ 
    # create table 
    fig, ax = plt.subplots() 
    N, bins, patches = ax.hist(final_marks, 
           align='mid', 
           bins=np.sort(np.append(marks, 6.))) # bins=marks 
    ax.xaxis.set_major_formatter(FormatStrFormatter('%0.1f')) 
    bin_centers = 0.5 * np.diff(bins) + bins[:-1] 
    ax.set_xticks(bin_centers) 
    ax.set_xticklabels(marks) 
    ax.set_xlabel("mark") 
    ax.set_ylabel("number of marks") 
    ax.set_ylim(0.0, 6.0) 
    plt.grid(True) 

def get_marks_interact(min_perc_four = 0.15, 
         min_perc_one = 0.85,): 
    return get_marks(min_perc_four, min_perc_one) 

interact(get_marks_wrapper, 
     min_perc_four=(0.2, 0.4, 0.05), 
     min_perc_one=(0.75, 0.95, 0.05)); 
+0

Я изменил 'make_figure' иметь только именованные аргументы в его определении, и в' interact', я определить диапазон только для переменных, значение которых я хочу изменить (например, в вашем втором блоке кода). Теперь я получаю ошибку 'ValueError: массив <'const_1'> не может быть преобразован в виджет' Выполняет ли' 'взаимодействие'' автоматически только на фигуре matplotlib, определенной в вызывающей функции? То есть неважно, что в 'make_figure' рассчитывается множество вещей (например,' b' и 'c')? – Claus

+0

Я добавил минимальный пример. Ваше текущее выполнение 'make_figure' создает новый показатель каждый раз, когда параметр изменяется в' взаимодействии'. Поэтому, если вы не представите воспроизводимый пример, чтобы проиллюстрировать проблему, пытающуюся отладить ее, это игра с угадыванием. – Primer

+0

с вашим минимальным примером, он работает. Тем не менее, я все еще придерживаюсь своей первоначальной проблемы. Следовательно, я добавил немного более длинный пример, включая сообщение об ошибке. Почему эта ошибка смотрит на переменную 'ids_perc', когда я действительно хочу взаимодействовать с фигурой? – Claus