// Numbas version: finer_feedback_settings {"name": "Two-way frequency table", "extensions": ["sheets"], "custom_part_types": [{"source": {"pk": 242, "author": {"name": "Christian Lawson-Perfect", "pk": 7}, "edit_page": "/part_type/242/edit"}, "name": "Spreadsheet", "short_name": "spreadsheet", "description": "

An editable spreadsheet. Ranges of cells can be disabled, and you can specify ranges of cells to be marked. A cell is marked correct if its value is equal to the value in the expected answer spreadsheet.

", "help_url": "", "input_widget": "spread-sheet", "input_options": {"correctAnswer": "settings[\"correct_answer\"]", "hint": {"static": true, "value": ""}, "initial_sheet": {"static": false, "value": "disable_cells(settings[\"initial_sheet\"], settings[\"disable_ranges\"])"}}, "can_be_gap": true, "can_be_step": true, "marking_script": "correctAnswer:\nsettings[\"correct_answer\"]\n\nmark:\nif(sum(mark_ranges)=0,\n incorrect(),\n apply(mark_ranges)\n)\n\ninterpreted_answer:\nstudentAnswer\n\nrange_cells:\nmap(parse_range(ref),ref,values(settings[\"mark_ranges\"]))\n\ntotal_cells:\nlen(flatten(range_cells))\n\nrange_weights:\nswitch(\n settings[\"marking_method\"]=\"per_cell\",\n map(len(r)/total_cells, r, range_cells),\n // otherwise, mark per range\n repeat(1/len(range_cells), len(range_cells))\n)\n\nmark_ranges:\nmap(\n let(\n range_credit,\n sum(map(\n let(\n correctCellString, correctAnswer[c],\n correctCellNumber, parsenumber(correctCellString, notation_styles),\n studentCellString, studentAnswer[c],\n studentCellNumber, parsenumber(studentCellString, notation_styles),\n award(\n 1/len(cells), \n if(isnan(correctCellNumber) and correctCellString<>\"\",\n lower(correctCellString) = lower(studentCellString),\n abs(studentCellNumber - if(isnan(correctCellNumber),0,correctCellNumber)) <= settings[\"tolerance\"]\n )\n )\n ),\n c,\n cells\n )),\n message,\n switch(\n range_credit=0,\n if(len(cells)=1, \"This entry is incorrect.\", \"All entries in this range are incorrect.\"),\n range_credit=1,\n if(len(cells)=1, \"This entry is correct.\", \"All entries in this range are correct.\"),\n //otherwise\n \"Some entries in this range are correct.\"\n ),\n assert(len(cells)=0, add_credit(range_credit*w, \"{name}: \"+message)); \n range_credit\n ),\n [cells,w,name],\n zip(range_cells, range_weights, keys(settings[\"mark_ranges\"]))\n)\n\nnotation_styles:\n[\"plain\",\"si-en\"]", "marking_notes": [{"name": "correctAnswer", "description": "

A spreadsheet representing the expected answer.

", "definition": "settings[\"correct_answer\"]"}, {"name": "mark", "description": "This is the main marking note. It should award credit and provide feedback based on the student's answer.", "definition": "if(sum(mark_ranges)=0,\n incorrect(),\n apply(mark_ranges)\n)"}, {"name": "interpreted_answer", "description": "A value representing the student's answer to this part.", "definition": "studentAnswer"}, {"name": "range_cells", "description": "

For each range to be marked, the addresses of the cells in that range.

", "definition": "map(parse_range(ref),ref,values(settings[\"mark_ranges\"]))"}, {"name": "total_cells", "description": "

The total number of cells to be marked. Cells in overlapping ranges will be counted once for each range they're in.

", "definition": "len(flatten(range_cells))"}, {"name": "range_weights", "description": "

The weight of each range, as a proportion of the available credit.

", "definition": "switch(\n settings[\"marking_method\"]=\"per_cell\",\n map(len(r)/total_cells, r, range_cells),\n // otherwise, mark per range\n repeat(1/len(range_cells), len(range_cells))\n)"}, {"name": "mark_ranges", "description": "

Mark each of the ranges specified by the question author.

", "definition": "map(\n let(\n range_credit,\n sum(map(\n let(\n correctCellString, correctAnswer[c],\n correctCellNumber, parsenumber(correctCellString, notation_styles),\n studentCellString, studentAnswer[c],\n studentCellNumber, parsenumber(studentCellString, notation_styles),\n award(\n 1/len(cells), \n if(isnan(correctCellNumber) and correctCellString<>\"\",\n lower(correctCellString) = lower(studentCellString),\n abs(studentCellNumber - if(isnan(correctCellNumber),0,correctCellNumber)) <= settings[\"tolerance\"]\n )\n )\n ),\n c,\n cells\n )),\n message,\n switch(\n range_credit=0,\n if(len(cells)=1, \"This entry is incorrect.\", \"All entries in this range are incorrect.\"),\n range_credit=1,\n if(len(cells)=1, \"This entry is correct.\", \"All entries in this range are correct.\"),\n //otherwise\n \"Some entries in this range are correct.\"\n ),\n assert(len(cells)=0, add_credit(range_credit*w, \"{name}: \"+message)); \n range_credit\n ),\n [cells,w,name],\n zip(range_cells, range_weights, keys(settings[\"mark_ranges\"]))\n)"}, {"name": "notation_styles", "description": "

Accepted number notation styles for a value in an individual cell.

", "definition": "[\"plain\",\"si-en\"]"}], "settings": [{"name": "initial_sheet", "label": "Initial sheet", "help_url": "", "hint": "A spreadsheet object giving the initial state of the sheet that the student should fill in.", "input_type": "code", "default_value": "", "evaluate": true}, {"name": "correct_answer", "label": "Correct answer", "help_url": "", "hint": "A spreadsheet object representing a correct answer to the part.", "input_type": "code", "default_value": "", "evaluate": true}, {"name": "disable_ranges", "label": "Ranges to disable", "help_url": "", "hint": "A list of cell or range references, denoting the cells that should not be editable.", "input_type": "code", "default_value": "[]", "evaluate": true}, {"name": "mark_ranges", "label": "Ranges to mark", "help_url": "", "hint": "A dictionary of cell or range references, mapping names to ranges of cells, denoting the cells that should be compared for equality with the expected answer.", "input_type": "code", "default_value": "dict()", "evaluate": true}, {"name": "marking_method", "label": "Marking method", "help_url": "", "hint": "", "input_type": "dropdown", "default_value": "per_cell", "choices": [{"value": "per_cell", "label": "Each cell has the same weight"}, {"value": "per_range", "label": "Each range has the same weight"}]}, {"name": "tolerance", "label": "Allowed margin of error", "help_url": "", "hint": "", "input_type": "code", "default_value": "0", "evaluate": true}], "public_availability": "always", "published": true, "extensions": ["sheets"]}], "resources": [], "navigation": {"allowregen": true, "showfrontpage": false, "preventleave": false, "typeendtoleave": false}, "question_groups": [{"pickingStrategy": "all-ordered", "questions": [{"name": "Two-way frequency table", "tags": [], "metadata": {"description": "

This demonstrates how to:

\n", "licence": "Creative Commons Attribution 4.0 International"}, "statement": "

{n} people were asked what kind of drink they prefer.

\n

In a real question, the following information would be given in a more roundabout way:

\n

{table}

", "advice": "", "rulesets": {}, "extensions": ["sheets"], "builtin_constants": {"e": true, "pi,\u03c0": true, "i": true, "j": false}, "constants": [], "variables": {"frequencies": {"name": "frequencies", "group": "Ungrouped variables", "definition": "matrix(random_integer_partition(n, n_1 * n_2) |> groups_of(n_1))", "description": "

Frequencies for each of the possible combinations of the two categories.

", "templateType": "anything", "can_override": false}, "table": {"name": "table", "group": "Ungrouped variables", "definition": "table(entries, options_1+[\"Total\"], options_2+[\"Total\"])", "description": "

A two-way probability table showing the probability for each combination of the two categories.

", "templateType": "anything", "can_override": false}, "options_1": {"name": "options_1", "group": "Ungrouped variables", "definition": "[ \"Coffee\", \"Tea\", \"Hot chocolate\", \"Water\" ]", "description": "

Options for the first category.

", "templateType": "list of strings", "can_override": false}, "options_2": {"name": "options_2", "group": "Ungrouped variables", "definition": "[ \"Student\", \"Staff\" ]", "description": "

Options for the second category.

", "templateType": "list of strings", "can_override": false}, "entries": {"name": "entries", "group": "Ungrouped variables", "definition": "let(\n rows, (row + [sum(row)] for: row of: frequencies), // For each row, the data plus their total\n column_totals, sum(row) for: row of: transpose(frequencies), // The total for each data column\n total_row, column_totals+[sum(column_totals)], // The final row is the total for each column, including the grand total.\n rows + [total_row]\n)", "description": "

Entries for the table: the frequency data with an extra column and row added for totals.

", "templateType": "anything", "can_override": false}, "empty_sheet": {"name": "empty_sheet", "group": "Ungrouped variables", "definition": "spreadsheet()\n|> fill_range(encode_range(1,0,n_1+1,0), options_1+[\"Total\"])\n|> fill_range(encode_range(0,1,0,n_2+1), options_2+[\"Total\"])\n|> update_range(encode_range(0,0,n_1+1,0), font_style(\"bold\") |> border(\"bottom\",\"thin\"))\n|> update_range(encode_range(0,0,0,n_2+1), font_style(\"bold\") |> border(\"right\",\"thin\"))", "description": "", "templateType": "anything", "can_override": false}, "n_1": {"name": "n_1", "group": "Ungrouped variables", "definition": "round(len(options_1))", "description": "

The number of options in the first category

", "templateType": "anything", "can_override": false}, "n_2": {"name": "n_2", "group": "Ungrouped variables", "definition": "round(len(options_2))", "description": "

The number of options in the second category.

", "templateType": "anything", "can_override": false}, "completed_sheet": {"name": "completed_sheet", "group": "Ungrouped variables", "definition": "empty_sheet\n|> fill_range(data_range,entries)", "description": "", "templateType": "anything", "can_override": false}, "n": {"name": "n", "group": "Ungrouped variables", "definition": "100", "description": "

The total number of observations.

", "templateType": "anything", "can_override": false}, "data_range": {"name": "data_range", "group": "Ungrouped variables", "definition": "encode_range(1,1,n_1+1,n_2+1)", "description": "

The range in the spreadsheet containing the data.

", "templateType": "anything", "can_override": false}}, "variablesTest": {"condition": "", "maxRuns": 100}, "ungrouped_variables": ["n", "frequencies", "options_1", "options_2", "n_1", "n_2", "table", "entries", "empty_sheet", "data_range", "completed_sheet"], "variable_groups": [], "functions": {}, "preamble": {"js": "", "css": ""}, "parts": [{"type": "spreadsheet", "useCustomName": false, "customName": "", "marks": "15", "scripts": {}, "customMarkingAlgorithm": "", "extendBaseMarkingAlgorithm": true, "unitTests": [], "showCorrectAnswer": true, "showFeedbackIcon": true, "variableReplacements": [], "variableReplacementStrategy": "originalfirst", "nextParts": [], "suggestGoingBack": false, "adaptiveMarkingPenalty": 0, "exploreObjective": null, "prompt": "

Fill in this two-way frequency table using the data given above.

", "settings": {"initial_sheet": "empty_sheet", "correct_answer": "completed_sheet", "disable_ranges": "[encode_range(0,0,n_1+1,0)\n,encode_range(0,0,0,n_2+1)\n]", "mark_ranges": "[\"Data\": encode_range(1, 1, n_1, n_2)\n,\"Row totals\": encode_range(n_1+1, 1, n_1+1, n_2)\n,\"Column totals\": encode_range(1, n_2+1, n_1, n_2+1)\n,\"Grand total\": encode_range(n_1+1, n_2+1, n_1+1, n_2+1)\n]", "marking_method": "per_cell", "tolerance": "0"}}], "partsMode": "all", "maxMarks": 0, "objectives": [], "penalties": [], "objectiveVisibility": "always", "penaltyVisibility": "always", "contributors": [{"name": "Christian Lawson-Perfect", "profile_url": "http://clppc.local:8000/accounts/profile/1/"}, {"name": "Christian Lawson-Perfect", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/7/"}]}]}], "contributors": [{"name": "Christian Lawson-Perfect", "profile_url": "http://clppc.local:8000/accounts/profile/1/"}, {"name": "Christian Lawson-Perfect", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/7/"}]}