From 984369b6de094c1bb3d1618e6bc0c209c45e677e Mon Sep 17 00:00:00 2001 From: Rick Feng Date: Wed, 26 Jan 2022 19:06:54 -0800 Subject: [PATCH 01/10] Content quiz anaylsis code competed Comment and readme file is still required --- content_quiz_analysis.py | 7 ++ scripts/__init__.py | 1 + .../analysis_on_content_quiz.py | 69 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 content_quiz_analysis.py create mode 100644 scripts/content_quiz_analysis/analysis_on_content_quiz.py diff --git a/content_quiz_analysis.py b/content_quiz_analysis.py new file mode 100644 index 0000000..8f53cd2 --- /dev/null +++ b/content_quiz_analysis.py @@ -0,0 +1,7 @@ +from util import ReadingLogsData +from scripts import content_quiz_analysis + +if __name__ == '__main__': + reading_logs_data = ReadingLogsData() + reading_dict, quiz_dict = reading_logs_data.get_parsed_reading_log_data() + content_quiz_analysis(quiz_dict) \ No newline at end of file diff --git a/scripts/__init__.py b/scripts/__init__.py index 069d7fe..721c8e2 100644 --- a/scripts/__init__.py +++ b/scripts/__init__.py @@ -11,3 +11,4 @@ from .student_grouping import group_students from .analyze_module_feedback_survey import analyze_module_feedback, compare_module_feedback from .reading_logs import analyze_num_paragraphs, parse_reading_logs_module, parse_reading_logs_all +from .content_quiz_analysis.analysis_on_content_quiz import content_quiz_analysis diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py new file mode 100644 index 0000000..a407f78 --- /dev/null +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -0,0 +1,69 @@ +import matplotlib.pyplot as plt +import pandas as pd +import numpy as np + +def content_quiz_analysis(content_quiz_dict): + num_attempt_to_correct = {} + for reading_log_headers, pages in content_quiz_dict.items(): + number_of_entries = 0 + num_attempt = [] + for row in pages.iterrows(): + for series in row: + if isinstance(series, pd.Series): + for index, elements in series.items(): + if isinstance(elements, str): + number_of_entries += 1 + num_attempt.append(1) + elif isinstance(elements, list): + if isinstance(elements[0], str): + number_of_entries += 1 + individual_attempts = 0 + for submit in elements: + if submit != 'ans': + individual_attempts += 1 + else: + individual_attempts += 1 + num_attempt.append(individual_attempts) + break + + performance_list = [number_of_entries, num_attempt, sum(num_attempt)] + num_attempt_to_correct[reading_log_headers] = performance_list + print(reading_log_headers) + + # Overall content quiz attempt and individual content quiz attemp + overall_num_attempt = [] + individual_content_quiz_attempt = {} + overall_content_quiz_attempt = [] + content_quiz_attempt_mean = [] + content_quiz_attempt_std = [] + content_quiz_attempt_pages = [] + for page, value in num_attempt_to_correct.items(): + if value[0] != 0: + overall_num_attempt.extend(value[1]) + individual_content_quiz_attempt_mean = np.mean(value[1]) + individual_content_quiz_attempt_std = np.std(value[1]) + individual_content_quiz_attempt[page] = [individual_content_quiz_attempt_mean, individual_content_quiz_attempt_std] + content_quiz_attempt_mean.append(individual_content_quiz_attempt_mean) + content_quiz_attempt_std.append(individual_content_quiz_attempt_std) + content_quiz_attempt_pages.append(page) + else: + individual_content_quiz_attempt[page] = [0, 0] + + overall_content_quiz_attempt.extend([np.mean(overall_num_attempt), np.std(overall_num_attempt)]) + content_quiz_attempt_mean.append(overall_content_quiz_attempt[0]) + content_quiz_attempt_std.append(overall_content_quiz_attempt[1]) + content_quiz_attempt_pages.append('Overall') + #print(individual_content_quiz_attempt) + #print(overall_content_quiz_attempt) + + fig, ax1 = plt.subplots(1) + ax1.barh(content_quiz_attempt_pages, content_quiz_attempt_mean, xerr=content_quiz_attempt_std, capsize=3) + ax1.set_ylabel("individual page") + ax1.set_xlabel("Attempt") + ax1.set_title('Mean and standard deviation of content quiz attempt') + fig.tight_layout() + #plt.show() + + + + \ No newline at end of file From 0c2dc4a70879654357671679f80d8fcb0bd7e5cc Mon Sep 17 00:00:00 2001 From: Rick Feng Date: Wed, 26 Jan 2022 19:09:18 -0800 Subject: [PATCH 02/10] Create __init__.py --- scripts/content_quiz_analysis/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/content_quiz_analysis/__init__.py diff --git a/scripts/content_quiz_analysis/__init__.py b/scripts/content_quiz_analysis/__init__.py new file mode 100644 index 0000000..e69de29 From c8a508c66406511037547d57afe1b1f23383aa6c Mon Sep 17 00:00:00 2001 From: Rick Feng Date: Wed, 26 Jan 2022 19:12:41 -0800 Subject: [PATCH 03/10] Update analysis_on_content_quiz.py --- scripts/content_quiz_analysis/analysis_on_content_quiz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index a407f78..13c3e10 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -28,7 +28,7 @@ def content_quiz_analysis(content_quiz_dict): performance_list = [number_of_entries, num_attempt, sum(num_attempt)] num_attempt_to_correct[reading_log_headers] = performance_list - print(reading_log_headers) + #print(reading_log_headers) # Overall content quiz attempt and individual content quiz attemp overall_num_attempt = [] @@ -62,7 +62,7 @@ def content_quiz_analysis(content_quiz_dict): ax1.set_xlabel("Attempt") ax1.set_title('Mean and standard deviation of content quiz attempt') fig.tight_layout() - #plt.show() + plt.show() From fb8a0e1abb1b0a305adafb93f29077055a4ab071 Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Mon, 6 Dec 2021 01:45:03 -0800 Subject: [PATCH 04/10] write util function for checking if a file is a reading log. --- .../parse_reading_logs/parse_reading_logs.py | 12 +++--------- util/__init__.py | 5 +++-- util/reading_logs.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py b/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py index d144ae1..71441dd 100644 --- a/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py +++ b/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py @@ -5,7 +5,7 @@ import pandas as pd from schemas import CourseSchema -from util import ReadingLogsData +from util import ReadingLogsData, is_reading_log_file def parse_reading_logs_all(reading_log_path, module_paragraph_json_path) -> (dict, dict): @@ -104,14 +104,10 @@ def parsing_each_continue(reading_log_folder_path: str, module_number: str, data for reading_log in os.listdir(reading_log_folder_path): reading_log_path = os.path.join(reading_log_folder_path, reading_log) - if not reading_log.endswith('.json'): + if not is_reading_log_file(reading_log): continue reading_log_name_array = reading_log.split('-') - if '(' in reading_log and ')' in reading_log: # check for duplicate files - continue - if reading_log_name_array[0] != "COSC341": # make sure the file is a reading_log - continue if reading_log_name_array[1] != module_number: # make sure the reading log file is for the correct module continue @@ -146,9 +142,7 @@ def parsing_each_quiz_submit(reading_log_folder_path: str, module_number: str, d for reading_log in os.listdir(reading_log_folder_path): reading_log_path = os.path.join(reading_log_folder_path, reading_log) - if not reading_log.endswith('.json'): - continue - if '(' in reading_log and ')' in reading_log: # check for duplicate files + if not is_reading_log_file(reading_log): continue reading_log_name_array = reading_log.split('-') diff --git a/util/__init__.py b/util/__init__.py index cf62cc9..ff2edd3 100644 --- a/util/__init__.py +++ b/util/__init__.py @@ -4,6 +4,7 @@ from .const import KEY_PATH, MODULE_PARAGRAPHS_OUTPUT_FILEPATH, CACHE_FOLDER from .canvas_api import setup_submissions_filepath, get_quiz_id_from_file_name, DateTimeEncoder from .data_cleaner import keep_latest_survey_attempt -from .util import normalize +from .reading_logs import ReadingLogsData, page_reading_duration, module_reading_duration, get_text_difficulty_index, is_reading_log_file from .plots import set_plot_settings -from .reading_logs import ReadingLogsData +from .util import normalize + diff --git a/util/reading_logs.py b/util/reading_logs.py index dc97967..b683105 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -1,6 +1,7 @@ import json import os import statistics +from random import random import dill @@ -190,6 +191,23 @@ def page_reading_duration(self, module_num: int, page_num: int, data448_id: int return mean_duration, mean_duration_std + def is_reading_log_file(reading_log_file_name) -> bool: + if not reading_log_file_name.endswith('.json'): + return False + if '(' in reading_log_file_name and ')' in reading_log_file_name: # check for duplicate files + return False + + reading_log_name_array = reading_log_file_name.split('-') + # print(reading_log_name_array) + if reading_log_name_array[0] != "COSC341" & reading_log_name_array[3] != "Reading" & reading_log_name_array[ + 4] != "Logs": # make sure the file is a reading_log + return False + + def page_reading_duration(module_num: int, page_num: int, data448_id: int = None) -> float: + """Returns the page reading duration in minutes. Average of all students unless given a data_448 id.""" + # TODO: get average student reading duration for this page + return random() + def page_reading_duration_list(self, module_num: int, page_num: int) -> [float]: """Returns a list of student page reading duration in minutes""" reading_duration_dict = self.get_reading_duration_dict() From aafaf3f7973e56841ca93377c40abfae301c4f14 Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Fri, 4 Feb 2022 04:33:40 -0800 Subject: [PATCH 05/10] make analysis_on_content_quiz.py use util function to calculate content_quiz attempts. Clean up graph generated. Change using Statistic.stdev to numpy.std due to statistical rounding error. --- .../analysis_on_content_quiz.py | 86 +++++++------------ .../parse_reading_logs/parse_reading_logs.py | 12 ++- util/__init__.py | 2 +- util/reading_logs.py | 3 +- 4 files changed, 41 insertions(+), 62 deletions(-) diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index 13c3e10..a3071c9 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -1,69 +1,41 @@ import matplotlib.pyplot as plt import pandas as pd import numpy as np +from util import ReadingLogsData + def content_quiz_analysis(content_quiz_dict): - num_attempt_to_correct = {} - for reading_log_headers, pages in content_quiz_dict.items(): - number_of_entries = 0 - num_attempt = [] - for row in pages.iterrows(): - for series in row: - if isinstance(series, pd.Series): - for index, elements in series.items(): - if isinstance(elements, str): - number_of_entries += 1 - num_attempt.append(1) - elif isinstance(elements, list): - if isinstance(elements[0], str): - number_of_entries += 1 - individual_attempts = 0 - for submit in elements: - if submit != 'ans': - individual_attempts += 1 - else: - individual_attempts += 1 - num_attempt.append(individual_attempts) - break - - performance_list = [number_of_entries, num_attempt, sum(num_attempt)] - num_attempt_to_correct[reading_log_headers] = performance_list - #print(reading_log_headers) - - # Overall content quiz attempt and individual content quiz attemp - overall_num_attempt = [] - individual_content_quiz_attempt = {} - overall_content_quiz_attempt = [] + r = ReadingLogsData() content_quiz_attempt_mean = [] content_quiz_attempt_std = [] content_quiz_attempt_pages = [] - for page, value in num_attempt_to_correct.items(): - if value[0] != 0: - overall_num_attempt.extend(value[1]) - individual_content_quiz_attempt_mean = np.mean(value[1]) - individual_content_quiz_attempt_std = np.std(value[1]) - individual_content_quiz_attempt[page] = [individual_content_quiz_attempt_mean, individual_content_quiz_attempt_std] - content_quiz_attempt_mean.append(individual_content_quiz_attempt_mean) - content_quiz_attempt_std.append(individual_content_quiz_attempt_std) + + for page in content_quiz_dict.keys(): + split_key = page.split('-') + module_num = split_key[0] + page_num = split_key[1] + page_attempt = r.page_content_quiz_num_attempts(module_num=module_num, page_num=page_num) + if page_attempt is not None: + page_attempt_mean = page_attempt[0] + page_attempt_std = page_attempt[1] content_quiz_attempt_pages.append(page) - else: - individual_content_quiz_attempt[page] = [0, 0] - - overall_content_quiz_attempt.extend([np.mean(overall_num_attempt), np.std(overall_num_attempt)]) - content_quiz_attempt_mean.append(overall_content_quiz_attempt[0]) - content_quiz_attempt_std.append(overall_content_quiz_attempt[1]) + content_quiz_attempt_mean.append(page_attempt_mean) + content_quiz_attempt_std.append(page_attempt_std) + + content_quiz_attempt_mean.append(np.mean(content_quiz_attempt_mean)) + content_quiz_attempt_std.append(np.std(content_quiz_attempt_std)) content_quiz_attempt_pages.append('Overall') - #print(individual_content_quiz_attempt) - #print(overall_content_quiz_attempt) - - fig, ax1 = plt.subplots(1) - ax1.barh(content_quiz_attempt_pages, content_quiz_attempt_mean, xerr=content_quiz_attempt_std, capsize=3) - ax1.set_ylabel("individual page") - ax1.set_xlabel("Attempt") - ax1.set_title('Mean and standard deviation of content quiz attempt') - fig.tight_layout() + + print(content_quiz_attempt_std) + + plt.scatter(content_quiz_attempt_pages, content_quiz_attempt_mean) + plt.errorbar(content_quiz_attempt_pages, content_quiz_attempt_mean, yerr=content_quiz_attempt_std, + fmt='o', ecolor="skyblue", elinewidth=0.5) + plt.xticks(fontsize=5) + plt.xlabel("Module_number-Page_number") + plt.ylabel("Number of attempts") + plt.title("Mean and Standard Deviation of Content Quiz Attempts for Each Page") plt.show() - - - \ No newline at end of file + + diff --git a/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py b/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py index 71441dd..82d6227 100644 --- a/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py +++ b/scripts/reading_logs/parse_reading_logs/parse_reading_logs.py @@ -1,3 +1,5 @@ +import pandas as pd +import os import json import math import os @@ -5,7 +7,7 @@ import pandas as pd from schemas import CourseSchema -from util import ReadingLogsData, is_reading_log_file +from util import ReadingLogsData def parse_reading_logs_all(reading_log_path, module_paragraph_json_path) -> (dict, dict): @@ -104,7 +106,9 @@ def parsing_each_continue(reading_log_folder_path: str, module_number: str, data for reading_log in os.listdir(reading_log_folder_path): reading_log_path = os.path.join(reading_log_folder_path, reading_log) - if not is_reading_log_file(reading_log): + r = ReadingLogsData() + + if not r.is_reading_log_file(reading_log): continue reading_log_name_array = reading_log.split('-') @@ -142,7 +146,9 @@ def parsing_each_quiz_submit(reading_log_folder_path: str, module_number: str, d for reading_log in os.listdir(reading_log_folder_path): reading_log_path = os.path.join(reading_log_folder_path, reading_log) - if not is_reading_log_file(reading_log): + r = ReadingLogsData() + + if not r.is_reading_log_file(reading_log): continue reading_log_name_array = reading_log.split('-') diff --git a/util/__init__.py b/util/__init__.py index ff2edd3..79632eb 100644 --- a/util/__init__.py +++ b/util/__init__.py @@ -4,7 +4,7 @@ from .const import KEY_PATH, MODULE_PARAGRAPHS_OUTPUT_FILEPATH, CACHE_FOLDER from .canvas_api import setup_submissions_filepath, get_quiz_id_from_file_name, DateTimeEncoder from .data_cleaner import keep_latest_survey_attempt -from .reading_logs import ReadingLogsData, page_reading_duration, module_reading_duration, get_text_difficulty_index, is_reading_log_file +from .reading_logs import ReadingLogsData #, page_reading_duration, module_reading_duration, get_text_difficulty_index, is_reading_log_file from .plots import set_plot_settings from .util import normalize diff --git a/util/reading_logs.py b/util/reading_logs.py index b683105..9cd8195 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -1,6 +1,7 @@ import json import os import statistics +import numpy as np from random import random import dill @@ -244,7 +245,7 @@ def ms_to_minutes(duration_ms: float): def aggregate_and_sd(values: [], mean=True) -> (float, float): values_list = list(values) if len(values_list) > 1: - sd = statistics.stdev(values_list) + sd = np.std(values_list) else: sd = 0 if mean: From 6c3969be16c36c9798146d89fd69186e12bbd9af Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Fri, 4 Feb 2022 20:39:51 -0800 Subject: [PATCH 06/10] clean up code for analysis_on_content_quiz.py write new util funtions to compute first attempt grade on content quizzes --- content_quiz_analysis.py | 5 +- scripts/__init__.py | 2 +- .../analysis_on_content_quiz.py | 53 +++++++++++++++++-- util/reading_logs.py | 53 ++++++++++++++++++- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/content_quiz_analysis.py b/content_quiz_analysis.py index 8f53cd2..dbc8ac7 100644 --- a/content_quiz_analysis.py +++ b/content_quiz_analysis.py @@ -1,7 +1,8 @@ from util import ReadingLogsData -from scripts import content_quiz_analysis +from scripts import content_quiz_attempts_analysis, content_quiz_grade_analysis if __name__ == '__main__': reading_logs_data = ReadingLogsData() reading_dict, quiz_dict = reading_logs_data.get_parsed_reading_log_data() - content_quiz_analysis(quiz_dict) \ No newline at end of file + content_quiz_attempts_analysis(quiz_dict) + content_quiz_grade_analysis(quiz_dict) \ No newline at end of file diff --git a/scripts/__init__.py b/scripts/__init__.py index 3e1c296..41a246b 100644 --- a/scripts/__init__.py +++ b/scripts/__init__.py @@ -12,5 +12,5 @@ from .analyze_module_feedback_survey import analyze_module_feedback, compare_module_feedback from .reading_logs import analyze_num_paragraphs, parse_reading_logs_module, parse_reading_logs_all, \ reading_logs_completion_time -from .content_quiz_analysis.analysis_on_content_quiz import content_quiz_analysis +from .content_quiz_analysis.analysis_on_content_quiz import content_quiz_attempts_analysis, content_quiz_grade_analysis diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index a3071c9..8caaae5 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -1,20 +1,29 @@ import matplotlib.pyplot as plt -import pandas as pd import numpy as np from util import ReadingLogsData -def content_quiz_analysis(content_quiz_dict): +def content_quiz_attempts_analysis(content_quiz_dict): r = ReadingLogsData() content_quiz_attempt_mean = [] content_quiz_attempt_std = [] content_quiz_attempt_pages = [] + # module_paragraphs_dict = r.get_module_paragraphs_dict() + # for module_num, page_dict in module_paragraphs_dict.items(): + # for page_num, page_data in page_dict.items(): + # if not page_is_valid(module_num, page_data): + # continue + for page in content_quiz_dict.keys(): split_key = page.split('-') module_num = split_key[0] page_num = split_key[1] page_attempt = r.page_content_quiz_num_attempts(module_num=module_num, page_num=page_num) + + module_attempt = r.module_content_quiz_first_attempt_grade(module_num=module_num) + #TODO: loop through each module + if page_attempt is not None: page_attempt_mean = page_attempt[0] page_attempt_std = page_attempt[1] @@ -26,16 +35,50 @@ def content_quiz_analysis(content_quiz_dict): content_quiz_attempt_std.append(np.std(content_quiz_attempt_std)) content_quiz_attempt_pages.append('Overall') - print(content_quiz_attempt_std) - plt.scatter(content_quiz_attempt_pages, content_quiz_attempt_mean) plt.errorbar(content_quiz_attempt_pages, content_quiz_attempt_mean, yerr=content_quiz_attempt_std, fmt='o', ecolor="skyblue", elinewidth=0.5) plt.xticks(fontsize=5) plt.xlabel("Module_number-Page_number") plt.ylabel("Number of attempts") - plt.title("Mean and Standard Deviation of Content Quiz Attempts for Each Page") + plt.title("Mean and Standard Deviation of Extra Attempts for Each Content Quiz") + plt.show() + + +def content_quiz_grade_analysis(content_quiz_dict): + r = ReadingLogsData() + + content_quiz_grade_mean = [] + content_quiz_grade_std = [] + content_quiz_grade_pages = [] + + for page in content_quiz_dict.keys(): + split_key = page.split('-') + module_num = split_key[0] + page_num = split_key[1] + page_grade = r.page_content_quiz_first_attempt_grade(module_num=module_num, page_num=page_num) + + if page_grade is not None: + page_grade_mean = page_grade[0] + page_grade_std = page_grade[1] + content_quiz_grade_pages.append(page) + content_quiz_grade_mean.append(page_grade_mean) + content_quiz_grade_std.append(page_grade_std) + + content_quiz_grade_mean.append(np.mean(content_quiz_grade_mean)) + content_quiz_grade_std.append(np.std(content_quiz_grade_std)) + content_quiz_grade_pages.append('Overall') + + plt.scatter(content_quiz_grade_pages, content_quiz_grade_mean) + plt.errorbar(content_quiz_grade_pages, content_quiz_grade_mean, yerr=content_quiz_grade_std, + fmt='o', ecolor="skyblue", elinewidth=0.5) + plt.xticks(fontsize=5) + plt.xlabel("Module_number-Page_number") + plt.ylabel("First attempt average grade") + plt.title("Mean and Standard Deviation of First Attempt Grade for Each Content Quiz") plt.show() + + diff --git a/util/reading_logs.py b/util/reading_logs.py index 9cd8195..cdb1383 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -1,7 +1,7 @@ import json import os -import statistics import numpy as np +import pandas as pd from random import random import dill @@ -109,7 +109,7 @@ def page_content_quiz_num_attempts(self, module_num: int, page_num: int, data448 """ Retrieves the average number of content quiz attempts before a correct answer for a page. If given a data448_id, only retrieves that student's reading speed for the page. Without a data448_id, retrieves the - average reading speed of that page. + average number of attempts of that page. Returns None if this page has no content quiz questions. Standard deviation is None if a data448_id is given. @@ -160,6 +160,55 @@ def module_content_quiz_num_attempts(self, module_num: int, data448_id: int = No return aggregate_and_sd(content_quiz_attempts_per_page) + def page_content_quiz_first_attempt_grade(self, module_num: int, page_num: int, data448_id: int = None) -> float: + """ + Retrieves the average first attempt grade for content quiz. If given a data448_id, only retrieves that student's + reading speed for the page. Without a data448_id, retrieves the average first attempt grade of that page. + + Returns None if this page has no content quiz questions. + + :param module_num: The module number + :param page_num: The page number + :param data448_id: A student's Data 448 id + :return: (float representing the average first attempt grade of the given content quiz, standard deviation of + this average) + """ + + page_content_quiz_df = self.get_content_quiz_performance_dict()[f'{module_num}-{page_num}'] + + zip_set = [page_content_quiz_df[col] for col in page_content_quiz_df if col.startswith('q')] + + if not zip_set: + return None + + cols = [col for col in page_content_quiz_df if col.startswith('q')] + questions_df = page_content_quiz_df[cols] + questions_df = questions_df.reset_index() + questions_df = questions_df.T + + def first_attempt_grade(row): + count = 0 + for element in row: + if element == 'ans' or element[0] == 'ans': + count += 1 + return count/len(row) + + questions_df['first_attempt_grade'] = questions_df.apply(lambda x: first_attempt_grade(x), axis=1) + + if data448_id: + return questions_df['first_attempt_grade'][f'{data448_id}'], None + + return aggregate_and_sd(questions_df['first_attempt_grade']) + + def module_content_quiz_first_attempt_grade(self, module_num: int, data448_id: int = None) -> (float, float): + content_quiz_first_attempt_grade = [] + module_paragraphs_dict = self.get_module_paragraphs_dict() + for page_num in module_paragraphs_dict[str(module_num)].keys(): + average_num_attempts, _ = self.page_content_quiz_first_attempt_grade(module_num, int(page_num), data448_id) + content_quiz_first_attempt_grade.append(average_num_attempts) + + return aggregate_and_sd(content_quiz_first_attempt_grade) + def get_paragraph_list(self, module_num: int, page_num: int) -> [str]: module_paragraphs_dict = self.get_module_paragraphs_dict() page_paragraphs = [] From 8ef8d5f4fc56a1a24b1d205e3f836ccae17a864b Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Sun, 6 Feb 2022 05:14:15 -0800 Subject: [PATCH 07/10] refractor how grade and attempts are graphed added modular graph --- .../analysis_on_content_quiz.py | 142 +++++++++++------- util/reading_logs.py | 10 +- 2 files changed, 98 insertions(+), 54 deletions(-) diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index 8caaae5..5ec8033 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -1,42 +1,47 @@ import matplotlib.pyplot as plt import numpy as np from util import ReadingLogsData +from schemas import CourseSchema def content_quiz_attempts_analysis(content_quiz_dict): r = ReadingLogsData() - content_quiz_attempt_mean = [] - content_quiz_attempt_std = [] + + page_content_quiz_attempt_mean = [] + page_content_quiz_attempt_std = [] content_quiz_attempt_pages = [] - # module_paragraphs_dict = r.get_module_paragraphs_dict() - # for module_num, page_dict in module_paragraphs_dict.items(): - # for page_num, page_data in page_dict.items(): - # if not page_is_valid(module_num, page_data): - # continue - - for page in content_quiz_dict.keys(): - split_key = page.split('-') - module_num = split_key[0] - page_num = split_key[1] - page_attempt = r.page_content_quiz_num_attempts(module_num=module_num, page_num=page_num) - - module_attempt = r.module_content_quiz_first_attempt_grade(module_num=module_num) - #TODO: loop through each module - - if page_attempt is not None: - page_attempt_mean = page_attempt[0] - page_attempt_std = page_attempt[1] - content_quiz_attempt_pages.append(page) - content_quiz_attempt_mean.append(page_attempt_mean) - content_quiz_attempt_std.append(page_attempt_std) - - content_quiz_attempt_mean.append(np.mean(content_quiz_attempt_mean)) - content_quiz_attempt_std.append(np.std(content_quiz_attempt_std)) + module_content_quiz_attempt_mean = [] + module_content_quiz_attempt_std = [] + content_quiz_attempt_modules = [] + + module_paragraphs_dict = r.get_module_paragraphs_dict() + for module_num, page_dict in module_paragraphs_dict.items(): + for page_num, page_data in page_dict.items(): + if not CourseSchema.page_is_valid(module_num, page_data): + continue + + # set up for per page analysis + page_attempt = r.page_content_quiz_num_attempts(module_num=module_num, page_num=page_num) + if page_attempt is not None: + content_quiz_attempt_pages.append(str(module_num)+"-"+str(page_num)) + page_content_quiz_attempt_mean.append(page_attempt[0]) + page_content_quiz_attempt_std.append(page_attempt[1]) + + # set up for per module analysis + module_attempt = r.module_content_quiz_num_attempts(module_num) + if module_attempt is not None: + content_quiz_attempt_modules.append(str(module_num)) + module_content_quiz_attempt_mean.append(module_attempt[0]) + module_content_quiz_attempt_std.append(module_attempt[1]) + + # Per page graph + page_content_quiz_attempt_mean.append(np.mean(page_content_quiz_attempt_mean)) + page_content_quiz_attempt_std.append(np.std(page_content_quiz_attempt_std)) content_quiz_attempt_pages.append('Overall') - plt.scatter(content_quiz_attempt_pages, content_quiz_attempt_mean) - plt.errorbar(content_quiz_attempt_pages, content_quiz_attempt_mean, yerr=content_quiz_attempt_std, + plt.scatter(content_quiz_attempt_pages, page_content_quiz_attempt_mean) + plt.errorbar(content_quiz_attempt_pages, page_content_quiz_attempt_mean, yerr=page_content_quiz_attempt_std, fmt='o', ecolor="skyblue", elinewidth=0.5) plt.xticks(fontsize=5) plt.xlabel("Module_number-Page_number") @@ -44,41 +49,76 @@ def content_quiz_attempts_analysis(content_quiz_dict): plt.title("Mean and Standard Deviation of Extra Attempts for Each Content Quiz") plt.show() + # Per module graph + module_content_quiz_attempt_mean.append(np.mean(module_content_quiz_attempt_mean)) + module_content_quiz_attempt_std.append(np.std(module_content_quiz_attempt_std)) + content_quiz_attempt_modules.append('Overall') + + plt.scatter(content_quiz_attempt_modules, module_content_quiz_attempt_mean) + plt.errorbar(content_quiz_attempt_modules, module_content_quiz_attempt_mean, yerr=module_content_quiz_attempt_std, + fmt='o', ecolor="skyblue", elinewidth=0.5) + plt.xticks(fontsize=5) + plt.xlabel("Module_number") + plt.ylabel("Number of attempts") + plt.title("Mean and Standard Deviation of Extra Attempts for Each Module") + plt.show() + def content_quiz_grade_analysis(content_quiz_dict): r = ReadingLogsData() - content_quiz_grade_mean = [] - content_quiz_grade_std = [] + page_content_quiz_grade_mean = [] + page_content_quiz_grade_std = [] content_quiz_grade_pages = [] - for page in content_quiz_dict.keys(): - split_key = page.split('-') - module_num = split_key[0] - page_num = split_key[1] - page_grade = r.page_content_quiz_first_attempt_grade(module_num=module_num, page_num=page_num) - - if page_grade is not None: - page_grade_mean = page_grade[0] - page_grade_std = page_grade[1] - content_quiz_grade_pages.append(page) - content_quiz_grade_mean.append(page_grade_mean) - content_quiz_grade_std.append(page_grade_std) - - content_quiz_grade_mean.append(np.mean(content_quiz_grade_mean)) - content_quiz_grade_std.append(np.std(content_quiz_grade_std)) + module_content_quiz_grade_mean = [] + module_content_quiz_grade_std = [] + content_quiz_grade_modules = [] + + module_paragraphs_dict = r.get_module_paragraphs_dict() + for module_num, page_dict in module_paragraphs_dict.items(): + for page_num, page_data in page_dict.items(): + if not CourseSchema.page_is_valid(module_num, page_data): + continue + + # set up for per page analysis + page_grade = r.page_content_quiz_first_attempt_grade(module_num=module_num, page_num=page_num) + if page_grade is not None: + content_quiz_grade_pages.append(str(module_num) + "-" + str(page_num)) + page_content_quiz_grade_mean.append(page_grade[0]) + page_content_quiz_grade_std.append(page_grade[1]) + + # set up for per module analysis + module_grade = r.module_content_quiz_first_attempt_grade(module_num) + if module_grade is not None: + content_quiz_grade_modules.append(str(module_num)) + module_content_quiz_grade_mean.append(module_grade[0]) + module_content_quiz_grade_std.append(module_grade[1]) + + # Per page graph + page_content_quiz_grade_mean.append(np.mean(page_content_quiz_grade_mean)) + page_content_quiz_grade_std.append(np.std(page_content_quiz_grade_std)) content_quiz_grade_pages.append('Overall') - plt.scatter(content_quiz_grade_pages, content_quiz_grade_mean) - plt.errorbar(content_quiz_grade_pages, content_quiz_grade_mean, yerr=content_quiz_grade_std, + plt.scatter(content_quiz_grade_pages, page_content_quiz_grade_mean) + plt.errorbar(content_quiz_grade_pages, page_content_quiz_grade_mean, yerr=page_content_quiz_grade_std, fmt='o', ecolor="skyblue", elinewidth=0.5) plt.xticks(fontsize=5) plt.xlabel("Module_number-Page_number") - plt.ylabel("First attempt average grade") + plt.ylabel("Number of grades") plt.title("Mean and Standard Deviation of First Attempt Grade for Each Content Quiz") plt.show() + # Per module graph + module_content_quiz_grade_mean.append(np.mean(module_content_quiz_grade_mean)) + module_content_quiz_grade_std.append(np.std(module_content_quiz_grade_std)) + content_quiz_grade_modules.append('Overall') - - - + plt.scatter(content_quiz_grade_modules, module_content_quiz_grade_mean) + plt.errorbar(content_quiz_grade_modules, module_content_quiz_grade_mean, yerr=module_content_quiz_grade_std, + fmt='o', ecolor="skyblue", elinewidth=0.5) + plt.xticks(fontsize=5) + plt.xlabel("Module_number") + plt.ylabel("First attempt grades") + plt.title("Mean and Standard Deviation of First Attempt Grade for Each Module") + plt.show() diff --git a/util/reading_logs.py b/util/reading_logs.py index a5ab260..e987009 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -172,7 +172,9 @@ def module_content_quiz_num_attempts(self, module_num: int, data448_id: int = No content_quiz_attempts_per_page = [] module_paragraphs_dict = self.get_module_paragraphs_dict() for page_num in module_paragraphs_dict[str(module_num)].keys(): - average_num_attempts, _ = self.page_content_quiz_num_attempts(module_num, int(page_num), data448_id) + page_attempts = self.page_content_quiz_num_attempts(module_num, int(page_num), data448_id) + if page_attempts is not None: + average_num_attempts = page_attempts[0] content_quiz_attempts_per_page.append(average_num_attempts) return aggregate_and_sd(content_quiz_attempts_per_page) @@ -221,8 +223,10 @@ def module_content_quiz_first_attempt_grade(self, module_num: int, data448_id: i content_quiz_first_attempt_grade = [] module_paragraphs_dict = self.get_module_paragraphs_dict() for page_num in module_paragraphs_dict[str(module_num)].keys(): - average_num_attempts, _ = self.page_content_quiz_first_attempt_grade(module_num, int(page_num), data448_id) - content_quiz_first_attempt_grade.append(average_num_attempts) + page_grade = self.page_content_quiz_first_attempt_grade(module_num, int(page_num), data448_id) + if page_grade is not None: + average_grade = page_grade[0] + content_quiz_first_attempt_grade.append(average_grade) return aggregate_and_sd(content_quiz_first_attempt_grade) From 08fc3e09427c2118f8760d3dcd50b0948b619cd9 Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Mon, 7 Feb 2022 02:50:58 -0800 Subject: [PATCH 08/10] fix bug in how the first attempt average is calculated for content quiz. --- content_quiz_analysis.py | 2 +- schemas/course.py | 3 +- .../analysis_on_content_quiz.py | 47 +++++++--- util/reading_logs.py | 87 ++++++++++++++----- 4 files changed, 104 insertions(+), 35 deletions(-) diff --git a/content_quiz_analysis.py b/content_quiz_analysis.py index dbc8ac7..9a7b6e4 100644 --- a/content_quiz_analysis.py +++ b/content_quiz_analysis.py @@ -4,5 +4,5 @@ if __name__ == '__main__': reading_logs_data = ReadingLogsData() reading_dict, quiz_dict = reading_logs_data.get_parsed_reading_log_data() - content_quiz_attempts_analysis(quiz_dict) + # content_quiz_attempts_analysis(quiz_dict) content_quiz_grade_analysis(quiz_dict) \ No newline at end of file diff --git a/schemas/course.py b/schemas/course.py index 6816784..521a0ce 100644 --- a/schemas/course.py +++ b/schemas/course.py @@ -34,7 +34,8 @@ class CourseSchema: ] @staticmethod - def page_is_valid(module_num: int, page_num: int) -> bool: + def page_is_valid(module_num: str, page_num: str) -> bool: + module_num, page_num = int(module_num), int(page_num) if module_num in CourseSchema.CANCELLED_MODULES or \ module_num in CourseSchema.OPTIONAL_MODULES or \ module_num in CourseSchema.SUMMARY_MODULES: diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index 5ec8033..e7ede1a 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -15,14 +15,16 @@ def content_quiz_attempts_analysis(content_quiz_dict): module_content_quiz_attempt_std = [] content_quiz_attempt_modules = [] + all_attempts_list = [] + module_paragraphs_dict = r.get_module_paragraphs_dict() for module_num, page_dict in module_paragraphs_dict.items(): for page_num, page_data in page_dict.items(): - if not CourseSchema.page_is_valid(module_num, page_data): + if not CourseSchema.page_is_valid(module_num, page_num): continue # set up for per page analysis - page_attempt = r.page_content_quiz_num_attempts(module_num=module_num, page_num=page_num) + page_attempt = r.page_content_quiz_num_attempts(module_num, page_num) if page_attempt is not None: content_quiz_attempt_pages.append(str(module_num)+"-"+str(page_num)) page_content_quiz_attempt_mean.append(page_attempt[0]) @@ -35,11 +37,18 @@ def content_quiz_attempts_analysis(content_quiz_dict): module_content_quiz_attempt_mean.append(module_attempt[0]) module_content_quiz_attempt_std.append(module_attempt[1]) - # Per page graph - page_content_quiz_attempt_mean.append(np.mean(page_content_quiz_attempt_mean)) - page_content_quiz_attempt_std.append(np.std(page_content_quiz_attempt_std)) + # set up for overall analysis + all_attempts = r.page_content_quiz_attempts_list(module_num, page_num) + if all_attempts is not None: + all_attempts_list.extend(all_attempts) + + overall_mean = np.mean(all_attempts_list) + overall_std = np.std(all_attempts_list) + page_content_quiz_attempt_std.append(overall_std) + page_content_quiz_attempt_mean.append(overall_mean) content_quiz_attempt_pages.append('Overall') + # Per page graph plt.scatter(content_quiz_attempt_pages, page_content_quiz_attempt_mean) plt.errorbar(content_quiz_attempt_pages, page_content_quiz_attempt_mean, yerr=page_content_quiz_attempt_std, fmt='o', ecolor="skyblue", elinewidth=0.5) @@ -50,8 +59,8 @@ def content_quiz_attempts_analysis(content_quiz_dict): plt.show() # Per module graph - module_content_quiz_attempt_mean.append(np.mean(module_content_quiz_attempt_mean)) - module_content_quiz_attempt_std.append(np.std(module_content_quiz_attempt_std)) + module_content_quiz_attempt_mean.append(overall_mean) + module_content_quiz_attempt_std.append(overall_std) content_quiz_attempt_modules.append('Overall') plt.scatter(content_quiz_attempt_modules, module_content_quiz_attempt_mean) @@ -75,12 +84,17 @@ def content_quiz_grade_analysis(content_quiz_dict): module_content_quiz_grade_std = [] content_quiz_grade_modules = [] + overall_grade_list = [] + module_paragraphs_dict = r.get_module_paragraphs_dict() for module_num, page_dict in module_paragraphs_dict.items(): for page_num, page_data in page_dict.items(): - if not CourseSchema.page_is_valid(module_num, page_data): + if not CourseSchema.page_is_valid(module_num, page_num): continue + if module_num == 11 or module_num == 9: + a = 1 + # set up for per page analysis page_grade = r.page_content_quiz_first_attempt_grade(module_num=module_num, page_num=page_num) if page_grade is not None: @@ -95,11 +109,18 @@ def content_quiz_grade_analysis(content_quiz_dict): module_content_quiz_grade_mean.append(module_grade[0]) module_content_quiz_grade_std.append(module_grade[1]) - # Per page graph - page_content_quiz_grade_mean.append(np.mean(page_content_quiz_grade_mean)) - page_content_quiz_grade_std.append(np.std(page_content_quiz_grade_std)) + # set up for overall analysis + overall_grade = r.content_quiz_first_attempt_grade_list(module_num, page_num) + if overall_grade is not None: + overall_grade_list.extend(overall_grade) + + overall_mean = np.mean(overall_grade_list) + overall_std = np.std(overall_grade_list) + page_content_quiz_grade_mean.append(overall_mean) + page_content_quiz_grade_std.append(overall_std) content_quiz_grade_pages.append('Overall') + # Per page graph plt.scatter(content_quiz_grade_pages, page_content_quiz_grade_mean) plt.errorbar(content_quiz_grade_pages, page_content_quiz_grade_mean, yerr=page_content_quiz_grade_std, fmt='o', ecolor="skyblue", elinewidth=0.5) @@ -110,8 +131,8 @@ def content_quiz_grade_analysis(content_quiz_dict): plt.show() # Per module graph - module_content_quiz_grade_mean.append(np.mean(module_content_quiz_grade_mean)) - module_content_quiz_grade_std.append(np.std(module_content_quiz_grade_std)) + module_content_quiz_grade_mean.append(overall_mean) + module_content_quiz_grade_std.append(overall_std) content_quiz_grade_modules.append('Overall') plt.scatter(content_quiz_grade_modules, module_content_quiz_grade_mean) diff --git a/util/reading_logs.py b/util/reading_logs.py index e987009..784ee27 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -12,6 +12,7 @@ START_TIME_KEY = 'start_time' END_TIME_KEY = 'end_time' DURATION_KEY = 'duration' +TAMPERED = -1 class ReadingLogsData: @@ -137,24 +138,6 @@ def page_content_quiz_num_attempts(self, module_num: int, page_num: int, data448 :return: (float representing the average number of content quiz attempts, standard deviation for this average) """ page_content_quiz_df = self.get_content_quiz_performance_dict()[f'{module_num}-{page_num}'] - TAMPERED = -1 - - def num_before_ans(*args): - q_response_lists = [] - for val in args: - q_response_lists.append(val) if isinstance(val, list) else q_response_lists.append([val]) - - # Discard if they tampered with the numbers so questions have a different number of attempts - if not all(len(response_set) == len(q_response_lists[0]) for response_set in q_response_lists): - return TAMPERED - - all_correct = ['ans'] * len(q_response_lists) - count = 0 - for q_response_set in zip(*q_response_lists): - if list(q_response_set) == all_correct: - return count / len(q_response_lists) - else: - count += 1 zip_set = [page_content_quiz_df[col] for col in page_content_quiz_df if col.startswith('q')] if not zip_set: @@ -179,6 +162,25 @@ def module_content_quiz_num_attempts(self, module_num: int, data448_id: int = No return aggregate_and_sd(content_quiz_attempts_per_page) + def page_content_quiz_attempts_list(self, module_num: int, page_num: int) -> [float]: + """ + returns a list of extra attempts each student took for the content quiz of the given page + :param module_num: The module number + :param page_num: The page number + :return: + """ + page_content_quiz_df = self.get_content_quiz_performance_dict()[f'{module_num}-{page_num}'] + + zip_set = [page_content_quiz_df[col] for col in page_content_quiz_df if col.startswith('q')] + if not zip_set: + return None + + page_content_quiz_df['num_before_ans'] = [num_before_ans(*a) for a in zip(*zip_set)] + + all_num_attempts = [num for num in page_content_quiz_df['num_before_ans'].values if num != TAMPERED] + + return all_num_attempts + def page_content_quiz_first_attempt_grade(self, module_num: int, page_num: int, data448_id: int = None) -> float: """ Retrieves the average first attempt grade for content quiz. If given a data448_id, only retrieves that student's @@ -202,8 +204,6 @@ def page_content_quiz_first_attempt_grade(self, module_num: int, page_num: int, cols = [col for col in page_content_quiz_df if col.startswith('q')] questions_df = page_content_quiz_df[cols] - questions_df = questions_df.reset_index() - questions_df = questions_df.T def first_attempt_grade(row): count = 0 @@ -230,6 +230,34 @@ def module_content_quiz_first_attempt_grade(self, module_num: int, data448_id: i return aggregate_and_sd(content_quiz_first_attempt_grade) + def content_quiz_first_attempt_grade_list(self, module_num: int, page_num: int): + """ + Returns the list of student's firat attempt grade for the given page + :param module_num: the module number + :param page_num: the page number + :return: + """ + page_content_quiz_df = self.get_content_quiz_performance_dict()[f'{module_num}-{page_num}'] + + zip_set = [page_content_quiz_df[col] for col in page_content_quiz_df if col.startswith('q')] + + if not zip_set: + return None + + cols = [col for col in page_content_quiz_df if col.startswith('q')] + questions_df = page_content_quiz_df[cols] + + def first_attempt_grade(row): + count = 0 + for element in row: + if element == 'ans' or element[0] == 'ans': + count += 1 + return count / len(row) + + questions_df['first_attempt_grade'] = questions_df.apply(lambda x: first_attempt_grade(x), axis=1) + + return [*questions_df['first_attempt_grade']] + def get_paragraph_list(self, module_num: int, page_num: int) -> [str]: module_paragraphs_dict = self.get_module_paragraphs_dict() page_paragraphs = [] @@ -323,3 +351,22 @@ def aggregate_and_sd(values: [], mean=True) -> (float, float): return mean, sd else: return sum(values_list), sd + + +def num_before_ans(*args): + q_response_lists = [] + for val in args: + q_response_lists.append(val) if isinstance(val, list) else q_response_lists.append([val]) + + # Discard if they tampered with the numbers so questions have a different number of attempts + if not all(len(response_set) == len(q_response_lists[0]) for response_set in q_response_lists): + return TAMPERED + + all_correct = ['ans'] * len(q_response_lists) + count = 0 + for q_response_set in zip(*q_response_lists): + if list(q_response_set) == all_correct: + return count / len(q_response_lists) + else: + count += 1 + From 7b5309bdaad5f0990a07a89a48527a6b2f7fa722 Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Mon, 7 Feb 2022 03:10:01 -0800 Subject: [PATCH 09/10] After discussion with opey, all overall calculations are compound calculations. Eg. overall on module graph is calculated by averaging the mean of each module and calculating the standard deviation of the mean of each module, rather than getting each student's submission and averaging it across all page and modules. --- content_quiz_analysis.py | 2 +- .../analysis_on_content_quiz.py | 41 +++++-------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/content_quiz_analysis.py b/content_quiz_analysis.py index 9a7b6e4..dbc8ac7 100644 --- a/content_quiz_analysis.py +++ b/content_quiz_analysis.py @@ -4,5 +4,5 @@ if __name__ == '__main__': reading_logs_data = ReadingLogsData() reading_dict, quiz_dict = reading_logs_data.get_parsed_reading_log_data() - # content_quiz_attempts_analysis(quiz_dict) + content_quiz_attempts_analysis(quiz_dict) content_quiz_grade_analysis(quiz_dict) \ No newline at end of file diff --git a/scripts/content_quiz_analysis/analysis_on_content_quiz.py b/scripts/content_quiz_analysis/analysis_on_content_quiz.py index e7ede1a..e4e4c28 100644 --- a/scripts/content_quiz_analysis/analysis_on_content_quiz.py +++ b/scripts/content_quiz_analysis/analysis_on_content_quiz.py @@ -15,8 +15,6 @@ def content_quiz_attempts_analysis(content_quiz_dict): module_content_quiz_attempt_std = [] content_quiz_attempt_modules = [] - all_attempts_list = [] - module_paragraphs_dict = r.get_module_paragraphs_dict() for module_num, page_dict in module_paragraphs_dict.items(): for page_num, page_data in page_dict.items(): @@ -37,18 +35,11 @@ def content_quiz_attempts_analysis(content_quiz_dict): module_content_quiz_attempt_mean.append(module_attempt[0]) module_content_quiz_attempt_std.append(module_attempt[1]) - # set up for overall analysis - all_attempts = r.page_content_quiz_attempts_list(module_num, page_num) - if all_attempts is not None: - all_attempts_list.extend(all_attempts) - - overall_mean = np.mean(all_attempts_list) - overall_std = np.std(all_attempts_list) - page_content_quiz_attempt_std.append(overall_std) - page_content_quiz_attempt_mean.append(overall_mean) + # Per page graph + page_content_quiz_attempt_std.append(np.mean(page_content_quiz_attempt_mean)) + page_content_quiz_attempt_mean.append(np.std(page_content_quiz_attempt_mean)) content_quiz_attempt_pages.append('Overall') - # Per page graph plt.scatter(content_quiz_attempt_pages, page_content_quiz_attempt_mean) plt.errorbar(content_quiz_attempt_pages, page_content_quiz_attempt_mean, yerr=page_content_quiz_attempt_std, fmt='o', ecolor="skyblue", elinewidth=0.5) @@ -59,8 +50,8 @@ def content_quiz_attempts_analysis(content_quiz_dict): plt.show() # Per module graph - module_content_quiz_attempt_mean.append(overall_mean) - module_content_quiz_attempt_std.append(overall_std) + module_content_quiz_attempt_mean.append(np.mean(module_content_quiz_attempt_mean)) + module_content_quiz_attempt_std.append(np.std(module_content_quiz_attempt_mean)) content_quiz_attempt_modules.append('Overall') plt.scatter(content_quiz_attempt_modules, module_content_quiz_attempt_mean) @@ -84,17 +75,12 @@ def content_quiz_grade_analysis(content_quiz_dict): module_content_quiz_grade_std = [] content_quiz_grade_modules = [] - overall_grade_list = [] - module_paragraphs_dict = r.get_module_paragraphs_dict() for module_num, page_dict in module_paragraphs_dict.items(): for page_num, page_data in page_dict.items(): if not CourseSchema.page_is_valid(module_num, page_num): continue - if module_num == 11 or module_num == 9: - a = 1 - # set up for per page analysis page_grade = r.page_content_quiz_first_attempt_grade(module_num=module_num, page_num=page_num) if page_grade is not None: @@ -109,18 +95,11 @@ def content_quiz_grade_analysis(content_quiz_dict): module_content_quiz_grade_mean.append(module_grade[0]) module_content_quiz_grade_std.append(module_grade[1]) - # set up for overall analysis - overall_grade = r.content_quiz_first_attempt_grade_list(module_num, page_num) - if overall_grade is not None: - overall_grade_list.extend(overall_grade) - - overall_mean = np.mean(overall_grade_list) - overall_std = np.std(overall_grade_list) - page_content_quiz_grade_mean.append(overall_mean) - page_content_quiz_grade_std.append(overall_std) + # Per page graph + page_content_quiz_grade_mean.append(np.mean(page_content_quiz_grade_mean)) + page_content_quiz_grade_std.append(np.std(page_content_quiz_grade_mean)) content_quiz_grade_pages.append('Overall') - # Per page graph plt.scatter(content_quiz_grade_pages, page_content_quiz_grade_mean) plt.errorbar(content_quiz_grade_pages, page_content_quiz_grade_mean, yerr=page_content_quiz_grade_std, fmt='o', ecolor="skyblue", elinewidth=0.5) @@ -131,8 +110,8 @@ def content_quiz_grade_analysis(content_quiz_dict): plt.show() # Per module graph - module_content_quiz_grade_mean.append(overall_mean) - module_content_quiz_grade_std.append(overall_std) + module_content_quiz_grade_mean.append(np.mean(module_content_quiz_grade_mean)) + module_content_quiz_grade_std.append(np.std(module_content_quiz_grade_mean)) content_quiz_grade_modules.append('Overall') plt.scatter(content_quiz_grade_modules, module_content_quiz_grade_mean) From 92d1ad9042a53d6df13e6086936754e452619d6b Mon Sep 17 00:00:00 2001 From: nononovia <44279056+nononovia@users.noreply.github.com> Date: Mon, 7 Feb 2022 03:20:44 -0800 Subject: [PATCH 10/10] pr changes --- util/__init__.py | 2 +- util/reading_logs.py | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/util/__init__.py b/util/__init__.py index 79632eb..37a3f29 100644 --- a/util/__init__.py +++ b/util/__init__.py @@ -4,7 +4,7 @@ from .const import KEY_PATH, MODULE_PARAGRAPHS_OUTPUT_FILEPATH, CACHE_FOLDER from .canvas_api import setup_submissions_filepath, get_quiz_id_from_file_name, DateTimeEncoder from .data_cleaner import keep_latest_survey_attempt -from .reading_logs import ReadingLogsData #, page_reading_duration, module_reading_duration, get_text_difficulty_index, is_reading_log_file +from .reading_logs import ReadingLogsData from .plots import set_plot_settings from .util import normalize diff --git a/util/reading_logs.py b/util/reading_logs.py index 784ee27..94c860d 100644 --- a/util/reading_logs.py +++ b/util/reading_logs.py @@ -158,7 +158,7 @@ def module_content_quiz_num_attempts(self, module_num: int, data448_id: int = No page_attempts = self.page_content_quiz_num_attempts(module_num, int(page_num), data448_id) if page_attempts is not None: average_num_attempts = page_attempts[0] - content_quiz_attempts_per_page.append(average_num_attempts) + content_quiz_attempts_per_page.append(average_num_attempts) return aggregate_and_sd(content_quiz_attempts_per_page) @@ -226,7 +226,7 @@ def module_content_quiz_first_attempt_grade(self, module_num: int, data448_id: i page_grade = self.page_content_quiz_first_attempt_grade(module_num, int(page_num), data448_id) if page_grade is not None: average_grade = page_grade[0] - content_quiz_first_attempt_grade.append(average_grade) + content_quiz_first_attempt_grade.append(average_grade) return aggregate_and_sd(content_quiz_first_attempt_grade) @@ -297,16 +297,10 @@ def is_reading_log_file(reading_log_file_name) -> bool: return False reading_log_name_array = reading_log_file_name.split('-') - # print(reading_log_name_array) if reading_log_name_array[0] != "COSC341" & reading_log_name_array[3] != "Reading" & reading_log_name_array[ 4] != "Logs": # make sure the file is a reading_log return False - def page_reading_duration(module_num: int, page_num: int, data448_id: int = None) -> float: - """Returns the page reading duration in minutes. Average of all students unless given a data_448 id.""" - # TODO: get average student reading duration for this page - return random() - def page_reading_duration_list(self, module_num: int, page_num: int) -> [float]: """Returns a list of student page reading duration in minutes""" reading_duration_dict = self.get_reading_duration_dict()