def get_roc_figure_firsthalf_lasthalf(self, trialarray, plainroc):
     if not trialarray or not self.num_blocks:
         return WARNING_INSUFFICIENT_DATA
     FIGSIZE = (FULLWIDTH_PLOT_WIDTH, FULLWIDTH_PLOT_WIDTH/2)
     html = u""
     fig = plt.figure(figsize=FIGSIZE)
     warned = False
     for half in range(2):
         ax = fig.add_subplot(1, 2, half+1)
         # ... rows, cols, plotnum (in reading order from 1)
         blocks = range(half * self.num_blocks/2,
                        self.num_blocks/(2 - half))
         rocinfo = self.get_roc_info(trialarray, blocks, None)
         if rocinfo["rating_out_of_range"]:
             return ERROR_RATING_OUT_OF_RANGE
         if rocinfo["rating_missing"] and not warned:
             html += WARNING_RATING_MISSING
             warned = True
         show_x_label = True
         show_y_label = (half == 0)
         subtitle = "First half" if half == 0 else "Second half"
         self.plot_roc(
             ax,
             rocinfo["count_stimulus"],
             rocinfo["count_nostimulus"],
             show_x_label,
             show_y_label,
             plainroc,
             subtitle
         )
     title = PLAIN_ROC_TITLE if plainroc else Z_ROC_TITLE
     fig.suptitle(title, weight="bold")
     html += get_html_from_pyplot_figure(fig)
     return html
 def get_roc_figure_by_group(self, trialarray, grouparray, plainroc):
     if not trialarray or not grouparray:
         return WARNING_INSUFFICIENT_DATA
     FIGSIZE = (FULLWIDTH_PLOT_WIDTH*2, FULLWIDTH_PLOT_WIDTH)
     html = u""
     fig = plt.figure(figsize=FIGSIZE)
     warned = False
     for groupnum in range(len(grouparray)):
         ax = fig.add_subplot(2, 4, groupnum+1)
         # ... rows, cols, plotnum (in reading order from 1)
         rocinfo = self.get_roc_info(trialarray, [], [groupnum])
         if rocinfo["rating_out_of_range"]:
             return ERROR_RATING_OUT_OF_RANGE
         if rocinfo["rating_missing"] and not warned:
             html += WARNING_RATING_MISSING
             warned = True
         show_x_label = (groupnum > 3)
         show_y_label = (groupnum % 4 == 0)
         subtitle = "Group {} (n = {})".format(groupnum, rocinfo["total_n"])
         self.plot_roc(
             ax,
             rocinfo["count_stimulus"],
             rocinfo["count_nostimulus"],
             show_x_label,
             show_y_label,
             plainroc,
             subtitle
         )
     title = PLAIN_ROC_TITLE if plainroc else Z_ROC_TITLE
     fig.suptitle(title, weight="bold")
     html += get_html_from_pyplot_figure(fig)
     return html
Exemple #3
0
    def get_task_html(self):
        a = self.attn_score()
        m = self.mem_score()
        f = self.fluency_score()
        l = self.lang_score()
        v = self.vsp_score()
        t = a + m + f + l + v
        figurehtml = ""
        if self.is_complete():
            FIGSIZE = (FULLWIDTH_PLOT_WIDTH/3, FULLWIDTH_PLOT_WIDTH/4)
            WIDTH = 0.9
            fig = plt.figure(figsize=FIGSIZE)
            ax = fig.add_subplot(1, 1, 1)
            scores = numpy.array([a, m, f, l, v])
            maxima = numpy.array([18, 26, 14, 26, 16])
            y = 100 * scores/maxima
            x_labels = ["Attn", "Mem", "Flu", "Lang", "VSp"]
            N = len(y)
            xvar = numpy.arange(N)
            ax.bar(xvar, y, WIDTH, color="b")
            ax.set_ylabel("%")
            ax.set_xticks(xvar + WIDTH/2)
            ax.set_xticklabels(x_labels)
            ax.set_xlim(0 - (1 - WIDTH), len(scores))
            plt.tight_layout()  # or the ylabel drops off the figure
            # fig.autofmt_xdate()
            figurehtml = get_html_from_pyplot_figure(fig)
        return (
            self.get_standard_clinician_block(True, self.comments)
            + u"""
                <div class="summary">
                    <table class="summary">
                        <tr>
                            {is_complete}
                            <td class="figure" rowspan="7">{figure}</td>
                        </td>
            """.format(is_complete=self.get_is_complete_td_pair(),
                       figure=figurehtml)
            + tr("Total ACE-III score <sup>[1]</sup>", answer(t) + " / 100")
            + tr("Attention", answer(a) + " / 18 ({}%)".format(100*a/18))
            + tr("Memory", answer(m) + " / 26 ({}%)".format(100*m/26))
            + tr("Fluency", answer(f) + " / 14 ({}%)".format(100*f/14))
            + tr("Language", answer(l) + " / 26 ({}%)".format(100*l/26))
            + tr("Visuospatial", answer(v) + " / 16 ({}%)".format(100*v/16))
            + u"""
                    </table>
                </div>
                <table class="taskdetail">
                    <tr>
                        <th width="75%">Question</th>
                        <th width="25%">Answer/score</td>
                    </tr>
            """
            + tr_qa("Age on leaving full-time education",
                    self.age_at_leaving_full_time_education)
            + tr_qa("Occupation", ws.webify(self.occupation))
            + tr_qa("Handedness", ws.webify(self.handedness))

            + subheading_spanning_two_columns("Attention")
            + tr("Day? Date? Month? Year? Season?",
                 u", ".join([answer(x) for x in [self.attn_time1,
                                                 self.attn_time2,
                                                 self.attn_time3,
                                                 self.attn_time4,
                                                 self.attn_time5]]))
            + tr("House number/floor? Street/hospital? Town? County? Country?",
                 u", ".join([answer(x) for x in [self.attn_place1,
                                                 self.attn_place2,
                                                 self.attn_place3,
                                                 self.attn_place4,
                                                 self.attn_place5]]))
            + tr("Repeat: Lemon? Key? Ball?",
                 u", ".join([answer(x) for x in [self.attn_repeat_word1,
                                                 self.attn_repeat_word2,
                                                 self.attn_repeat_word3]]))
            + tr("Repetition: number of trials <i>(not scored)</i>",
                 answer(self.attn_num_registration_trials,
                        formatter_answer=italic))
            + tr(
                "Serial subtractions: First correct? Second? Third? Fourth? "
                "Fifth?",
                u", ".join([answer(x) for x in [
                    self.attn_serial7_subtraction1,
                    self.attn_serial7_subtraction2,
                    self.attn_serial7_subtraction3,
                    self.attn_serial7_subtraction4,
                    self.attn_serial7_subtraction5]]))

            + subheading_spanning_two_columns("Memory (1)")
            + tr("Recall: Lemon? Key? Ball?",
                 u", ".join([answer(x) for x in [self.mem_recall_word1,
                                                 self.mem_recall_word2,
                                                 self.mem_recall_word3]]))

            + subheading_spanning_two_columns("Fluency")
            + tr(u"Score for words beginning with ‘P’ <i>(≥18 scores 7, 14–17 "
                 u"scores 6, 11–13 scores 5, 8–10 scores 4, 6–7 scores 3, "
                 u"4–5 scores 2, 2–3 scores 1, 0–1 scores 0)</i>",
                 answer(self.fluency_letters_score) + " / 7")
            + tr(u"Score for animals <i>(≥22 scores 7, 17–21 scores 6, "
                 u"14–16 scores 5, 11–13 scores 4, 9–10 scores 3, "
                 u"7–8 scores 2, 5–6 scores 1, &lt;5 scores 0)</i>",
                 answer(self.fluency_animals_score) + " / 7")

            + subheading_spanning_two_columns("Memory (2)")
            + tr(
                "Third trial of address registration: Harry? Barnes? 73? "
                "Orchard? Close? Kingsbridge? Devon?",
                u", ".join([answer(x) for x in [
                    self.mem_repeat_address_trial3_1,
                    self.mem_repeat_address_trial3_2,
                    self.mem_repeat_address_trial3_3,
                    self.mem_repeat_address_trial3_4,
                    self.mem_repeat_address_trial3_5,
                    self.mem_repeat_address_trial3_6,
                    self.mem_repeat_address_trial3_7]]))
            + tr("Current PM? Woman who was PM? USA president? USA president "
                 "assassinated in 1960s?",
                 u", ".join([answer(x) for x in [self.mem_famous1,
                                                 self.mem_famous2,
                                                 self.mem_famous3,
                                                 self.mem_famous4]]))

            + subheading_spanning_two_columns("Language")
            + tr(u"<i>Practice trial (“Pick up the pencil and then the "
                 u"paper”)</i>",
                 answer(self.lang_follow_command_practice,
                        formatter_answer=italic))
            + tr_qa(u"“Place the paper on top of the pencil”",
                    self.lang_follow_command1)
            + tr_qa(u"“Pick up the pencil but not the paper”",
                    self.lang_follow_command2)
            + tr_qa(u"“Pass me the pencil after touching the paper”",
                    self.lang_follow_command3)
            + tr(
                u"Sentence-writing: point for ≥2 complete sentences about "
                u"the one topic? Point for correct grammar and spelling?",
                u", ".join([answer(x) for x in [
                    self.lang_write_sentences_point1,
                    self.lang_write_sentences_point2]]))
            + tr(
                u"Repeat: caterpillar? eccentricity? unintelligible? "
                u"statistician? <i>(score 2 if all correct, 1 if 3 correct, "
                u"0 if ≤2 correct)</i>",
                u"<i>{}, {}, {}, {}</i> (score <b>{}</b> / 2)".format(
                    answer(self.lang_repeat_word1, formatter_answer=italic),
                    answer(self.lang_repeat_word2, formatter_answer=italic),
                    answer(self.lang_repeat_word3, formatter_answer=italic),
                    answer(self.lang_repeat_word4, formatter_answer=italic),
                    self.get_repeat_word_score(),
                ))
            + tr_qa(u"Repeat: “All that glitters is not gold”?",
                    self.lang_repeat_sentence1)
            + tr_qa(u"Repeat: “A stitch in time saves nine”?",
                    self.lang_repeat_sentence2)
            + tr("Name pictures: spoon, book, kangaroo/wallaby",
                 u", ".join([answer(x) for x in [self.lang_name_picture1,
                                                 self.lang_name_picture2,
                                                 self.lang_name_picture3]]))
            + tr("Name pictures: penguin, anchor, camel/dromedary",
                 u", ".join([answer(x) for x in [self.lang_name_picture4,
                                                 self.lang_name_picture5,
                                                 self.lang_name_picture6]]))
            + tr("Name pictures: harp, rhinoceros/rhino, barrel/keg/tub",
                 u", ".join([answer(x) for x in [self.lang_name_picture7,
                                                 self.lang_name_picture8,
                                                 self.lang_name_picture9]]))
            + tr("Name pictures: crown, alligator/crocodile, "
                 "accordion/piano accordion/squeeze box",
                 u", ".join([answer(x) for x in [self.lang_name_picture10,
                                                 self.lang_name_picture11,
                                                 self.lang_name_picture12]]))
            + tr(
                "Identify pictures: monarchy? marsupial? Antarctic? nautical?",
                u", ".join([answer(x) for x in [self.lang_identify_concept1,
                                                self.lang_identify_concept2,
                                                self.lang_identify_concept3,
                                                self.lang_identify_concept4]]))
            + tr_qa("Read all successfully: sew, pint, soot, dough, height",
                    self.lang_read_words_aloud)

            + subheading_spanning_two_columns("Visuospatial")
            + tr("Copy infinity", answer(self.vsp_copy_infinity) + " / 1")
            + tr("Copy cube", answer(self.vsp_copy_cube) + " / 2")
            + tr("Draw clock with numbers and hands at 5:10",
                 answer(self.vsp_draw_clock) + " / 5")
            + tr("Count dots: 8, 10, 7, 9",
                 u", ".join([answer(x) for x in [self.vsp_count_dots1,
                                                 self.vsp_count_dots2,
                                                 self.vsp_count_dots3,
                                                 self.vsp_count_dots4]]))
            + tr("Identify letters: K, M, A, T",
                 u", ".join([answer(x) for x in [self.vsp_identify_letter1,
                                                 self.vsp_identify_letter2,
                                                 self.vsp_identify_letter3,
                                                 self.vsp_identify_letter4]]))

            + subheading_spanning_two_columns("Memory (3)")
            + tr("Recall address: Harry? Barnes? 73? Orchard? Close? "
                 "Kingsbridge? Devon?",
                 u", ".join([answer(x) for x in [self.mem_recall_address1,
                                                 self.mem_recall_address2,
                                                 self.mem_recall_address3,
                                                 self.mem_recall_address4,
                                                 self.mem_recall_address5,
                                                 self.mem_recall_address6,
                                                 self.mem_recall_address7]]))
            + tr("Recognize address: Jerry Barnes/Harry Barnes/Harry "
                 "Bradford?",
                 self.get_recog_text((self.mem_recall_address1 == 1
                                      and self.mem_recall_address2 == 1),
                                     self.mem_recognize_address1))
            + tr("Recognize address: 37/73/76?",
                 self.get_recog_text((self.mem_recall_address3 == 1),
                                     self.mem_recognize_address2))
            + tr(
                "Recognize address: Orchard Place/Oak Close/Orchard "
                "Close?",
                self.get_recog_text(
                    (self.mem_recall_address4 == 1
                     and self.mem_recall_address5 == 1),
                    self.mem_recognize_address3))
            + tr("Recognize address: Oakhampton/Kingsbridge/Dartington?",
                 self.get_recog_text((self.mem_recall_address6 == 1),
                                     self.mem_recognize_address4))
            + tr("Recognize address: Devon/Dorset/Somerset?",
                 self.get_recog_text((self.mem_recall_address7 == 1),
                                     self.mem_recognize_address5))

            + subheading_spanning_two_columns("Photos of test sheet")
            + tr_span_col(self.get_blob_png_html(self.picture1_blobid,
                                                 self.picture1_rotation),
                          td_class="photo")
            + tr_span_col(self.get_blob_png_html(self.picture2_blobid,
                                                 self.picture2_rotation),
                          td_class="photo")
            + u"""
                </table>
                <div class="footnotes">
                    [1] In the ACE-R (the predecessor of the ACE-III),
                    scores ≤82 had sensitivity 0.84 and specificity 1.0 for
                    dementia, and scores ≤88 had sensitivity 0.94 and
                    specificity 0.89 for dementia, in a context of patients
                    with AlzD, FTD, LBD, MCI, and controls
                    (Mioshi et al., 2006, PMID 16977673).
                </div>
                <div class="copyright">
                    ACE-III: Copyright © 2012, John Hodges.
                    “The ACE-III is available for free. The copyright is held
                    by Professor John Hodges who is happy for the test to be
                    used in clinical practice and research projects. There is
                    no need to contact us if you wish to use the ACE-III in
                    clinical practice.”
                    (ACE-III FAQ, 7 July 2013, www.neura.edu.au).
                </div>
            """
        )
    def get_trial_html(self):

        # Fetch trial details
        trialarray = self.get_trial_array()

        # Provide HTML
        html = Cardinal_ExpDetThreshold_Trial.get_html_table_header()
        for t in trialarray:
            html += t.get_html_table_row()
        html += u"""</table>"""

        # Don't add figures if we're incomplete
        if not self.is_complete():
            return html

        # Add figures

        FIGSIZE = (FULLWIDTH_PLOT_WIDTH/2, FULLWIDTH_PLOT_WIDTH/2)
        JITTER_STEP = 0.02
        DP_TO_CONSIDER_SAME_FOR_JITTER = 3
        Y_EXTRA_SPACE = 0.1
        X_EXTRA_SPACE = 0.02
        trialfig = plt.figure(figsize=FIGSIZE)
        notcalc_detected_x = []
        notcalc_detected_y = []
        notcalc_missed_x = []
        notcalc_missed_y = []
        calc_detected_x = []
        calc_detected_y = []
        calc_missed_x = []
        calc_missed_y = []
        catch_detected_x = []
        catch_detected_y = []
        catch_missed_x = []
        catch_missed_y = []
        all_x = []
        all_y = []
        for t in trialarray:
            x = t.trial
            y = t.intensity
            all_x.append(x)
            all_y.append(y)
            if t.trial_num_in_calculation_sequence is not None:
                if t.yes:
                    calc_detected_x.append(x)
                    calc_detected_y.append(y)
                else:
                    calc_missed_x.append(x)
                    calc_missed_y.append(y)
            elif t.target_presented:
                if t.yes:
                    notcalc_detected_x.append(x)
                    notcalc_detected_y.append(y)
                else:
                    notcalc_missed_x.append(x)
                    notcalc_missed_y.append(y)
            else:  # catch trial
                if t.yes:
                    catch_detected_x.append(x)
                    catch_detected_y.append(y)
                else:
                    catch_missed_x.append(x)
                    catch_missed_y.append(y)
        plt.plot(all_x,              all_y,              marker="",
                 color="0.9", linestyle="-", label=None)
        plt.plot(notcalc_missed_x,   notcalc_missed_y,   marker="o",
                 color="k",   linestyle="None", label="miss")
        plt.plot(notcalc_detected_x, notcalc_detected_y, marker="+",
                 color="k",   linestyle="None", label="hit")
        plt.plot(calc_missed_x,      calc_missed_y,      marker="o",
                 color="r",   linestyle="None", label="miss, scored")
        plt.plot(calc_detected_x,    calc_detected_y,    marker="+",
                 color="b",   linestyle="None", label="hit, scored")
        plt.plot(catch_missed_x,     catch_missed_y,     marker="o",
                 color="w",   linestyle="None", label="CR")
        plt.plot(catch_detected_x,   catch_detected_y,   marker="*",
                 color="w",   linestyle="None", label="FA")
        leg = plt.legend(
            numpoints=1,
            fancybox=True,  # for set_alpha (below)
            loc="best",  # bbox_to_anchor=(0.75, 1.05)
            labelspacing=0,
            handletextpad=0
        )
        leg.get_frame().set_alpha(0.5)
        plt.xlabel("Trial number")
        plt.ylabel("Intensity")
        plt.ylim(0 - Y_EXTRA_SPACE, 1 + Y_EXTRA_SPACE)
        plt.xlim(-0.5, len(trialarray) - 0.5)

        fitfig = None
        if self.k is not None and self.theta is not None:
            fitfig = plt.figure(figsize=FIGSIZE)
            detected_x = []
            detected_x_approx = []
            detected_y = []
            missed_x = []
            missed_x_approx = []
            missed_y = []
            all_x = []
            for t in trialarray:
                if t.trial_num_in_calculation_sequence is not None:
                    all_x.append(t.intensity)
                    approx_x = "{0:.{precision}f}".format(
                        t.intensity,
                        precision=DP_TO_CONSIDER_SAME_FOR_JITTER
                    )
                    if t.yes:
                        detected_y.append(1 - detected_x_approx.count(approx_x)
                                          * JITTER_STEP)
                        detected_x.append(t.intensity)
                        detected_x_approx.append(approx_x)
                    else:
                        missed_y.append(0 + missed_x_approx.count(approx_x)
                                        * JITTER_STEP)
                        missed_x.append(t.intensity)
                        missed_x_approx.append(approx_x)
            fit_x = numpy.arange(0.0 - X_EXTRA_SPACE, 1.0 + X_EXTRA_SPACE,
                                 0.001)
            fit_y = rnc_plot.logistic(fit_x, self.k, self.theta)
            plt.plot(fit_x,      fit_y,
                     color="g", linestyle="-")
            plt.plot(missed_x,   missed_y,   marker="o",
                     color="r", linestyle="None")
            plt.plot(detected_x, detected_y, marker="+",
                     color="b", linestyle="None")
            plt.ylim(0 - Y_EXTRA_SPACE, 1 + Y_EXTRA_SPACE)
            plt.xlim(numpy.amin(all_x) - X_EXTRA_SPACE,
                     numpy.amax(all_x) + X_EXTRA_SPACE)
            marker_points = []
            for y in (LOWER_MARKER, 0.5, UPPER_MARKER):
                x = rnc_plot.inv_logistic(y, self.k, self.theta)
                marker_points.append((x, y))
            for p in marker_points:
                plt.plot([p[0], p[0]], [-1, p[1]], color="0.5", linestyle=":")
                plt.plot([-1, p[0]], [p[1], p[1]], color="0.5", linestyle=":")
            plt.xlabel("Intensity")
            plt.ylabel("Detected? (0=no, 1=yes; jittered)")

        html += u"""
            <table class="noborder">
                <tr>
                    <td class="noborderphoto">{}</td>
                    <td class="noborderphoto">{}</td>
                </tr>
            </table>
        """.format(
            get_html_from_pyplot_figure(trialfig),
            get_html_from_pyplot_figure(fitfig)
        )

        return html