1

Ich möchte ein interaktives Plot in jupyter (4.0.6) notebook mit matplotlib (1.5.1) haben. Die Sache ist, dass das statische Plot mit einer Funktion erstellt wird, die vier Variablen hat, zwei davon sind Konstanten, zwei davon sind Schlüsselwortargumente und ich möchte die Schlüsselwortargumente interaktiv ändern.jupyter notebook: Aktualisiere Plot interaktiv - Funktion hat Konstanten und Schlüsselwortargumente

Ist das möglich, und wenn ja, wie?

Der folgende konzeptionelle Code zeigt die Funktion, die ein Plot make_figure(...) und den Befehl zum Generieren eines interaktiven Plots generiert.

Wenn ich die Keyword-Argumente auf Variablen ändern, dann erhalte ich die Fehlermeldung "interagieren() nimmt von 0 bis 1 Positionsargument aber 3 gegeben wurden"

konzeptueller Code:

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)) 

zusätzlich 20.160.325: Codebeispiel

ich ein Histogramm für Markierungen für eine Klasse, abhängig von th zu schaffen versuchen Der erforderliche Prozentsatz, um 1,0 bzw. 4,0 zu erreichen.

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

der Hauptcode für das Histogramm; Hauptfunktion: 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) 

Nun, wenn ich Setup versuchen interact tun dies

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

Ich erhalte die Fehler

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 

Warum dieser Fehler bei der variablen ids_perc suchen ist?

Antwort

1

Sie müssen Ihre Variablen explizit in interact() zuweisen. Zum Beispiel wie folgt aus:

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)) 

oder (wenn möglich) die Unterschrift des make_figure ändern diese Variablen in Keyword-Argumente zu machen, so dass Sie sie vermeiden können vorbei explizit:

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)) 

Hier MCWE dass Sie versuchen:

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)); 

Dies läuft ohne Fehler.

Auf Ihrem addition 20160325:

Jeder Parameter, den Sie interagieren passieren wird durch eines der darstellbaren sein müssen (es etwas vereinfacht):

  • einen Schieber (für tuple s, die darstellen, (min, max) und Skalarnummern)
  • Auswahlfeld (für Listen von Zeichenfolgen und Wörterbüchern)
  • Kontrollkästchen (für booleans)
  • ein Eingabefeld (für Streicher)

Sie passieren (implizit in Ihren get_marks zwei Parametern als np.arrays definiert wird). Also interact weiß nicht, wie man das auf einem Schieberegler darstellt, hense den Fehler.

Sie haben mindestens zwei Möglichkeiten:

1) die Unterschrift des get_marks zu ändern, so dass es Parameter nimmt die interact wird Aufzählungsliste oben)

2) machen eine weitere Wrapper-Funktion undetstand (siehe dass wird die Parameter nehmen, die interact untestands, aber get_marksnach Umwandlung dieser Parameter auf was auch immer get_marks braucht.

Also nur ein zusätzlicher Schritt und du bist fertig. ;-)

UPDATE:

Hier ist der Code mit Wrapper, der für mich arbeitet. Beachten Sie, dass get_marks_interact nicht alle Parameter von get_marks nehmen muss und ich keine Listen überlasse, da interact ein Problem mit ihnen haben wird (Liste sollte entweder eine Liste von Zeichenfolgen (für Dropdown-Widgets) oder Liste/Tupel von [min, max] Werte darstellen (für Schieberegler)).

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

geändert I 'make_figure' nur Keyword-Argumente in der Definition zu haben, und in' interact' definiere ich den Bereich nur für die Variablen, deren Wert I (wie in Ihrem zweiten Codeblock) ändern möchten. Jetzt bekomme ich den Fehler 'ValueError: Array von <'const_1'> kann nicht in ein Widget umgewandelt werden' Funktioniert 'interagieren' automatisch nur auf der Matplotlib Figur in der aufrufenden Funktion definiert? I.e. Es spielt keine Rolle, dass eine Vielzahl von Sachen in 'make_figure' (wie' b' und 'c') berechnet wird. – Claus

+0

Ich habe ein minimales Beispiel hinzugefügt. Ihre aktuelle Ausführung von 'make_figure' erzeugt jedes Mal eine neue Zahl, wenn ein Parameter in' interact' geändert wird. Wenn Sie also kein reproduzierbares Beispiel angeben, um ein Problem beim Debuggen zu veranschaulichen, ist das ein Ratespiel. – Primer

+0

mit Ihrem minimalen Beispiel funktioniert es. Allerdings bin ich immer noch mit meinem ursprünglichen Problem festgefahren. Daher habe ich ein etwas längeres Beispiel hinzugefügt, einschließlich der Fehlermeldung. Warum betrachtet dieser Fehler die Variable 'ids_perc', wenn ich wirklich mit der Figur interagieren will? – Claus