Plotting of and by students - All this
And now it’s all this
I just said what I said and it was wrong
Or was taken wrong
Next post<br>Previous post
Plotting of and by students
July 9, 2026 at 9:54 PM by Dr. Drang
I saw this article at Inside Higher Ed this morning, guided by a Mastodon post from Techmeme. The title of the article is "Brown Professor Suspects Majority of His Class Used AI to Cheat," so if you’re sick to death of reading about AI—pro, con, or caveated—don’t feel obligated to follow the link. I’m interested in a plot included in the article more than the article itself.
An economics professor gave his class a take-home midterm, and the grades on it were much higher than usual. He suspected the high marks came from the students using LLMs to answer the questions, so the final exam was done in class and the marks were generally much lower. Here’s the plot given in the article:
Image from Inside Higher Ed.
Let me start by saying I have no criticisms of the plot, just some comments about things that struck me.
First, the upper portion of the chart made me think the students, S1 through S59, were ordered according to their score on the final exam (the gray dots and figures). But as you go down the list, you soon see that that isn’t the case. After reading past the chart, I saw that the professor decided to throw out the results of the midterm and use the final exam as 80% of the course grade. Presumably, the students were sorted by their course grade.
More important, though, was the chart’s layout. When plotting a pair of scores for every student, the usual convention would be to have the students (the categories) laid out along the horizontal axis and their scores (the values) plotted on the vertical axis. This does it the other way around. There’s nothing wrong with doing it that way; it’s just unusual. Sort of like seeing a time series chart in which time is on the vertical axis. There can be good reasons to do it, but usually people don’t.
I first read the article on my phone, so I wondered if the layout was driven by the aspect ratio of most phones in portrait mode. In fact, since the chart is not actually an image but some sort of JavaScript thingy from Datawrapper. At least I think that’s what it is—I couldn’t select the chart as an image, and when I looked at the page’s HTML, I saw it was in an element.
This made me wonder if the chart would flip to a more conventional layout if the aspect ratio of the browser were different. Turning my phone to landscape mode didn’t flip the axes, nor did opening the page on my MacBook Pro with a wide Safari window. Clearly the author of the article, Emma Whitford, thought it was best to have the students running down the vertical axis.
I decided to see what a more conventional layout would look like. I used the link on the page to download the plot’s data as a CSV file—a very thoughtful addition to the article and something I wish more authors did—and whipped out a quick plot in Matplotlib. Here it is:
Even in a wide browser window, it’s pretty tightly constrained, mainly because I have a width limit on the content portion of ANIAT (that’s to keep lines of text of reasonable length). If you click on the chart, it’ll open to the full width of your browser window, which will make it easier to peruse.
Here’s the code that produced the chart:
python:<br>1: #!/usr/bin/env python3<br>2:<br>3: import pandas as pd<br>4: import numpy as np<br>5: import matplotlib.pyplot as plt<br>6: from matplotlib.ticker import MultipleLocator, AutoMinorLocator<br>7:<br>8: # Read in the exam scores<br>9: df = pd.read_csv('scores.csv')<br>10:<br>11: # Create the plot with a given size in inches<br>12: fig, ax = plt.subplots(figsize=(12, 6))<br>13:<br>14: # Bar colors are based on whether midterm was higher than final<br>15: colors = ['#0571b0']*59<br>16: for i in range(59):<br>17: if df.Final[i] > df.Midterm[i]:<br>18: colors[i] = '#ca0020'<br>19:<br>20: # Plot the scores as columns between the final and midterm scores<br>21: ax.bar(df.Student, df.Midterm-df.Final, bottom=df.Final, width=.5, color=colors, zorder=10)<br>22:<br>23: # Set the limits<br>24: plt.xlim(xmin=0, xmax=60)<br>25: plt.ylim(ymin=0, ymax=100)<br>26:<br>27: # Set the major and minor ticks and add a grid<br>28: ax.xaxis.set_major_locator(MultipleLocator(5))<br>29: ax.xaxis.set_minor_locator(AutoMinorLocator(5))<br>30: ax.yaxis.set_major_locator(MultipleLocator(20))<br>31: ax.yaxis.set_minor_locator(AutoMinorLocator(2))<br>32: ax.grid(linewidth=.5, axis='x', which='both', color='#dddddd', linestyle='-', zorder=0)<br>33: ax.grid(linewidth=.5, axis='y', which='both', color='#dddddd', linestyle='-', zorder=0)<br>34:<br>35: # Title and axis labels<br>36: plt.title('Final to midterm exam result ranges')<br>37: plt.xlabel('Student ID')<br>38: plt.ylabel('Score')<br>39:<br>40: # Make the border and tick marks 0.5 points wide<br>41: [ i.set_linewidth(0.5) for i in ax.spines.values() ]<br>42: ax.tick_params(which='both', width=.5)<br>43:<br>44: # Add a note<br>45: ax.text(5, 25, 'Midterms were higher than finals except for Student 22',...