flowchart TB A[Question and<br/>design<br/>Ch 5] --> B[Import<br/>Ch 1-2] B --> C[Clean and<br/>reshape<br/>Ch 3] C --> D[Explore and<br/>describe<br/>Ch 4, 6] D --> E[Test and<br/>model<br/>Ch 7-10] D --> F[Predict and<br/>discover<br/>Ch 11-16] E --> G[Report and<br/>share<br/>Ch 17-18] F --> G
19 Putting It All Together
A thesis is an argument, and its results chapter is the evidence. Each claim in it rests on a chain of reasoning that runs from a research question, through a hypothesis, a design, measured variables, and an analysis, to a result, a conclusion, and the limits of that conclusion. A thesis convinces when every link in that chain can be seen and checked, and every number in the text can be traced back to the raw data. The methods of this book are the links; this chapter joins them.
In the story, it is the final semester. Elaf’s analyses are spread over two years of scripts, written as she learned each method, and her supervisor’s advice for the last stretch is simple: “Before you write the results chapter, rebuild the whole thing, from the raw export to the last table, as one project that runs from start to finish. Then you will know that every number in your thesis is right, and you can answer any examiner’s question by pointing to the code.” This chapter does exactly that. It sets up a complete research project, runs the analysis from the messy survey export to the tables, figures, and sentences of a results chapter, and traces the chain of reasoning behind three of the research questions. It then steps back: how to report results to the standards journals expect, how to choose a method for a new question, the problems that every researcher meets, and where to go next.
- Organise a research project so that it runs from raw data to finished results.
- Combine cleaning, description, modelling, and visualisation in one reproducible pipeline.
- Trace the chain of reasoning from research question to conclusion and limitation.
- Report results in the style expected in a thesis, following published reporting standards, with numbers taken directly from the code.
- Choose an appropriate method for a research question, using the map of this book.
- Recognise common challenges in real research projects, and know where to learn more.
19.1 The shape of a research project
Every quantitative project follows roughly the same path, and this book has followed it too (Figure 19.1).
The path is not a straight line in practice. Exploring the data sends you back to cleaning when you find a problem; a model’s diagnostics send you back to exploring. What matters is that each step is written in code, so that going back and rerunning everything is easy.
19.1.1 Organising the project
A project that others (and your future self) can follow has a predictable structure. The final thesis project looks like this:
wellbeing-thesis/
├── README.txt what the project is and how to run it
├── data-raw/
│ └── wellbeing_raw.xlsx the survey export, never edited by hand
├── data/ clean data, created by the scripts
├── scripts/
│ ├── 01_clean_data.py raw export -> clean tables (Chapter 3)
│ └── 02_analysis.py models and figures
├── output/ figures and tables, created by the scripts
├── results.qmd the results chapter (Chapter 17)
├── references.bib
└── requirements.txt package versions (Chapter 17)
Four principles lie behind it. Raw data is read-only: everything in data/ and output/ can be deleted and recreated by running the scripts. Scripts are numbered in the order they run, and each does one job. Paths are relative to the project, using pathlib.Path (Chapter 1), so the project runs on any computer. And the README says in a few lines what the project is and how to run it.
The downloadable project for this chapter has the same structure, with working scripts (its data/ and output/ folders are created when the scripts run, and it provides a requirements.txt for virtual environments). The rest of the chapter walks through what the scripts do.
19.2 From raw export to clean data
The cleaning of Chapter 3, gathered into one script, runs in a few seconds. Here is its core, reading the raw export and producing the clean questionnaire and semester tables:
import pandas as pd
import numpy as np
raw = pd.read_excel(data2thesis_example("wellbeing_raw.xlsx"))
item_names = [f"stress_{i}" for i in range(1, 7)] + \
[f"burnout_{i}" for i in range(1, 7)] + \
[f"support_{i}" for i in range(1, 7)] + \
[f"satisfaction_{i}" for i in range(1, 5)]
# 1. Drop test responses and duplicates
responses = raw[~raw["Q1_Student ID"].str.upper().str.startswith("TEST")].copy()
responses = responses.drop(columns=["Response ID"]).drop_duplicates()
responses = responses.rename(columns={
"Q1_Student ID": "student_id",
"Workshop group": "workshop",
"Y1_Considered leaving?": "considering_dropout"
})
# Rename item columns Q11_1:Q11_22
q11_cols = [f"Q11_{i}" for i in range(1, 23)]
responses = responses.rename(columns=dict(zip(q11_cols, item_names)))
for col in item_names:
responses[col] = pd.to_numeric(responses[col].replace([99, "99"], np.nan), errors="coerce")
questionnaire_clean = responses[["student_id"] + item_names].copy()
# 2. Reshape semesters from wide to long
def parse_number(s):
return pd.to_numeric(s.astype(str).str.replace(",", ".").str.extract(r"(\d+(?:\.\d+)?)")[0], errors="coerce")
long_records = []
for sem in [1, 2, 3, 4]:
sub = pd.DataFrame({
"student_id": responses["student_id"],
"semester": sem,
"gpa": parse_number(responses[f"GPA_S{sem}"]),
"sleep_hours": parse_number(responses[f"Sleep_S{sem}"]),
"study_hours": parse_number(responses[f"Study_S{sem}"]),
"exercise_days": parse_number(responses[f"Exercise_S{sem}"]),
"caffeine_mg": parse_number(responses[f"Caffeine_S{sem}"]),
"supervisor_meetings": parse_number(responses[f"Meetings_S{sem}"]),
"wellbeing": parse_number(responses[f"Wellbeing_S{sem}"])
})
long_records.append(sub)
semesters_clean = pd.concat(long_records, ignore_index=True)
semesters_clean.loc[semesters_clean["sleep_hours"] > 24, "sleep_hours"] = np.nan
semesters_clean.loc[semesters_clean["study_hours"] > 168, "study_hours"] = np.nan
semesters_clean.loc[semesters_clean["gpa"] > 4, "gpa"] = np.nan
metric_cols = ["gpa", "sleep_hours", "study_hours", "exercise_days", "caffeine_mg", "supervisor_meetings", "wellbeing"]
semesters_clean = semesters_clean.dropna(subset=metric_cols, how="all")
semesters_clean["supervisor_meetings"] = semesters_clean["supervisor_meetings"].astype(int)
semesters_clean["wellbeing"] = semesters_clean["wellbeing"].astype(int)Each step is explained in Chapter 3; the full script in the downloadable project also cleans the background variables. The most important line of any cleaning script is the check at the end, here comparing with the clean reference data:
def check_match(mine, theirs, key):
m = mine.sort_values(key).reset_index(drop=True)
t = theirs[mine.columns].sort_values(key).reset_index(drop=True)
return m.equals(t)
print("Questionnaire matches:", check_match(questionnaire_clean, questionnaire, "student_id"))
print("Semesters matches: ", check_match(semesters_clean, semesters, ["student_id", "semester"]))Questionnaire matches: True
Semesters matches: True
Both are True. In a real project there is no package to compare with, so the checks are the ones from Chapters 3 and 6: counts of categories, ranges of values, numbers of missing values, and a look at a few rows. From here on, the chapter uses the clean tables, together with the scale scores:
scores = questionnaire.copy()
scores["stress_4"] = 6 - scores["stress_4"]
scores["stress"] = scores.filter(like="stress_").mean(axis=1)
scores["burnout"] = scores.filter(like="burnout_").mean(axis=1)
scores["support"] = scores.filter(like="support_").mean(axis=1)
scores = scores[["student_id", "stress", "burnout", "support"]]
study = students.merge(scores, on="student_id").merge(semesters.query("semester == 1"), on="student_id")
print(f"Total students in study dataset: {len(study)}")Total students in study dataset: 600
The table study has one row per student, with background, questionnaire scores, and first-semester records, as in Chapter 8.
19.3 Describing the sample
Every results chapter begins by describing the participants, often in a table known as “Table 1”. Here the sample is described by programme:
def mean_sd(x):
return f"{x.mean():.1f} ({x.std(ddof=1):.1f})"
def percent(x):
return f"{100 * x.mean():.0f}%"
def describe(d):
return pd.Series({
"Students": str(len(d)),
"Age, mean (SD)": mean_sd(d["age"]),
"Women": percent(d["gender"] == "Female"),
"Part-time": percent(d["study_mode"] == "Part-time"),
"Stress (1-5), mean (SD)": mean_sd(d["stress"]),
"Support (1-5), mean (SD)": mean_sd(d["support"]),
"Wellbeing in semester 1, mean (SD)": mean_sd(d["wellbeing"]),
"Considered dropping out": percent(d["considering_dropout"] == "Yes")
})
sample_desc = pd.DataFrame({
"Characteristic": describe(study).index,
"Master's": describe(study[study["programme"] == "Master's"]).values,
"PhD": describe(study[study["programme"] == "PhD"]).values,
"All": describe(study).values
})
print(sample_desc.to_markdown(index=False))| Characteristic | Master's | PhD | All |
|:-----------------------------------|:------------|:------------|:------------|
| Students | 426 | 174 | 600 |
| Age, mean (SD) | 27.9 (3.3) | 34.2 (5.1) | 29.7 (4.8) |
| Women | 52% | 53% | 52% |
| Part-time | 26% | 39% | 30% |
| Stress (1-5), mean (SD) | 3.2 (0.7) | 3.2 (0.7) | 3.2 (0.7) |
| Support (1-5), mean (SD) | 3.2 (0.8) | 3.2 (0.7) | 3.2 (0.8) |
| Wellbeing in semester 1, mean (SD) | 60.9 (12.1) | 59.3 (11.9) | 60.5 (12.0) |
| Considered dropping out | 15% | 14% | 15% |
Two small helper functions, mean_sd() and percent(), format the numbers the way theses report them, and describe() builds the column for any group of students, so the same code makes all three columns. Writing a small function whenever you would otherwise copy and paste code is one of the best habits to take from this book.
19.4 Answering the research questions
Three of the research questions are answered below with the methods of Parts 2 and 3, each with a sentence written the way it will appear in the thesis. A small function formats p-values in the usual style:
def format_p(p):
return "p < .001" if p < 0.001 else f"p = {p:.3f}".replace(" 0.", " .")19.4.1 The workshop and its persistence (RQ3)
Students were randomly invited to the wellbeing workshop after the first semester. The mixed-effects model of Chapter 10 uses all four semesters and every student:
panel = semesters.merge(students, on="student_id").assign(time=lambda d: d["semester"] - 1)
workshop_model = smf.mixedlm(
"wellbeing ~ C(semester) * workshop",
panel,
groups=panel["student_id"],
re_formula="~time"
).fit(method=["lbfgs", "powell"])
pred_grid = pd.MultiIndex.from_product(
[[1, 2, 3, 4], ["Invited", "Not invited"]],
names=["semester", "workshop"]
).to_frame().reset_index(drop=True)
pred_grid["time"] = pred_grid["semester"] - 1
pred_grid["predicted_wellbeing"] = workshop_model.predict(pred_grid)
piv = pred_grid.pivot(index="semester", columns="workshop", values="predicted_wellbeing")
gaps = piv["Invited"] - piv["Not invited"]
print("Wellbeing gaps across semesters:")
print(gaps.round(1))Wellbeing gaps across semesters:
semester
1 0.4
2 5.3
3 4.1
4 2.6
dtype: float64
Wellbeing was similar in the two groups before the workshop (difference 0.4 points). After the workshop, invited students’ wellbeing was 5.3 points higher in semester 2, but the difference narrowed to 4.1 points in semester 3 and 2.6 in semester 4.
19.4.2 Explaining GPA (RQ5)
The multiple regression of Chapter 8, with coefficients and confidence intervals organized as a clean summary:
gpa_model = smf.ols("gpa ~ sleep_hours + study_hours + stress + support", data=study).fit()
gpa_table = pd.DataFrame({
"estimate": gpa_model.params,
"std_err": gpa_model.bse,
"t_value": gpa_model.tvalues,
"p_value": gpa_model.pvalues,
"conf_low": gpa_model.conf_int()[0],
"conf_high": gpa_model.conf_int()[1]
})
print(gpa_table.round(3)) estimate std_err t_value p_value conf_low conf_high
Intercept 2.137 0.145 14.712 0.0 1.852 2.423
sleep_hours 0.106 0.014 7.487 0.0 0.078 0.134
study_hours 0.007 0.001 6.270 0.0 0.005 0.009
stress -0.084 0.018 -4.685 0.0 -0.119 -0.049
support 0.111 0.017 6.632 0.0 0.078 0.144
Each additional hour of sleep was associated with a GPA 0.11 points higher (95% CI 0.08 to 0.13, p < .001), holding study hours, stress, and support constant. Together, the four predictors explained 25% of the variation in first-semester GPA (n = 587).
19.4.3 Considering dropout (RQ9)
The logistic regression of Chapter 8, with odds ratios:
study["dropout_bool"] = (study["considering_dropout"] == "Yes").astype(int)
dropout_model = smf.logit(
"dropout_bool ~ stress + support + financial_worry + C(employment) + C(study_mode)",
data=study
).fit(disp=False)
dropout_table = pd.DataFrame({
"odds_ratio": np.exp(dropout_model.params),
"conf_low": np.exp(dropout_model.conf_int()[0]),
"conf_high": np.exp(dropout_model.conf_int()[1]),
"p_value": dropout_model.pvalues
})
print(dropout_table.round(2)) odds_ratio conf_low conf_high p_value
Intercept 0.02 0.00 0.20 0.00
C(employment)[T.None] 0.55 0.24 1.31 0.18
C(employment)[T.Part-time job] 0.58 0.24 1.42 0.23
C(study_mode)[T.Part-time] 1.32 0.64 2.71 0.45
stress 3.60 2.25 5.77 0.00
support 0.33 0.22 0.49 0.00
financial_worry 1.57 1.24 2.00 0.00
np.exp() turns the log-odds coefficients into odds ratios and their confidence intervals.
Each one-point increase in stress (on the 1 to 5 scale) was associated with 3.6 times the odds of considering dropping out (95% CI 2.3 to 5.8), and each one-point increase in supervisor support with 0.33 times the odds (95% CI 0.22 to 0.49).
Chapters 11 to 15 went further with this question, asking how well dropout can be predicted; the thesis reports that logistic regression predicted as well as machine learning models (a test AUC of about 0.84 in Chapter 11), which is itself a finding worth stating.
19.5 One figure for the thesis
A thesis figure often combines panels. Using matplotlib.pyplot.subplots(), we can join figures side by side with consistent styling:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4))
# Panel A: Workshop Trajectory
for w_label, color in [("Invited", navy), ("Not invited", copper)]:
sub = pred_grid[pred_grid["workshop"] == w_label]
ax1.plot(sub["semester"], sub["predicted_wellbeing"], marker="o", color=color, linewidth=2, label=w_label)
ax1.set_xlabel("Semester")
ax1.set_ylabel("Predicted wellbeing (0-100)")
ax1.set_xticks([1, 2, 3, 4])
ax1.legend(title="Workshop")
ax1.set_title("A", loc="left", fontweight="bold", fontsize=13)
# Panel B: Sleep and GPA
sns.regplot(
data=study, x="sleep_hours", y="gpa",
ax=ax2,
scatter_kws={"alpha": 0.3, "color": "black", "s": 20},
line_kws={"color": navy, "linewidth": 2}
)
ax2.set_xlabel("Sleep (hours a night)")
ax2.set_ylabel("GPA (semester 1)")
ax2.set_title("B", loc="left", fontweight="bold", fontsize=13)
for ax in (ax1, ax2):
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()
plt.savefig() saves the figure at the dimensions and resolution a thesis or journal requires (Chapter 4):
fig.savefig("output/figure-1.png", dpi=300, bbox_inches="tight")19.6 The chain of reasoning
A result on its own is not yet a conclusion. Between the two lie the design that produced the data, the way the variables were measured, and the limits of both. Table 19.2 traces the whole chain for the three research questions answered above, from the hypotheses stated in Chapter 5 to the limitations that a thesis discussion must acknowledge. Every result in it is filled in by the code of this chapter.
| Step | Workshop (RQ3) | GPA (RQ5) | Considering dropout (RQ9) |
|---|---|---|---|
| Question | Does the workshop improve wellbeing? | What explains students’ GPA? | Who considers dropping out? |
| Hypothesis (Chapter 5) | Invited students have higher wellbeing in semester 2 | More sleep goes with a higher GPA, allowing for study hours, stress, and support | Higher stress raises the odds of considering dropout |
| Design | Randomised invitation within a longitudinal study | Observational, first semester | Observational, baseline and end of year 1 |
| Variables | Wellbeing (0 to 100), invitation, semester | GPA, sleep, study hours, stress, support | Considering dropout (yes/no), stress, support, financial worry, employment, study mode |
| Analysis | Mixed-effects model (Chapter 10) | Multiple regression (Chapter 8) | Logistic regression (Chapter 8) |
| Result | 5.3 points higher in semester 2, narrowing to 2.6 by semester 4 | 0.11 GPA points per hour of sleep (95% CI 0.08 to 0.13) | Odds ratio 3.6 per point of stress (95% CI 2.2 to 5.8) |
| Conclusion | The workshop raised wellbeing, and the effect faded; because the invitation was random, the effect is causal | Sleep is associated with GPA, independently of the other predictors; the null hypothesis is rejected, but the design does not show cause | Stress is associated with higher odds of considering dropout; the null hypothesis is rejected |
| Limitation | One university; the invitation, not attendance, was randomised | Sleep is self-reported, and unmeasured confounders may remain | The outcome is considering dropout, not leaving; stress and the outcome were measured close together |
Reading the table by columns shows how different the three conclusions are, although all three rest on “significant” results. Only the workshop conclusion is causal, because only the workshop was assigned at random (Chapter 5). The GPA and dropout conclusions are associations, and the limitations say what could still explain them. A discussion chapter that keeps each result attached to its design, in this way, claims exactly what the evidence supports.
19.7 The results chapter
The final step is the one Chapter 17 prepared: the tables, figures, and sentences above go into a Quarto document, results.qmd, in which every number is written with inline code. The downloadable project contains such a document, built from this chapter.
What a results chapter must contain is not left to taste. Reporting standards list the information that readers need in order to judge a study. For quantitative research in psychology and neighbouring fields, the American Psychological Association’s Journal Article Reporting Standards (JARS) set out what to report about the participants, the measures, the analysis, and the results, including effect sizes and confidence intervals (Appelbaum et al. 2018). Randomised trials in health research follow CONSORT, and observational studies follow STROBE (von Elm et al. 2007). Checking a draft against the relevant standard is one of the most useful things a student can do before submission, and many journals require it. When the data changes, or an examiner asks for a different model, the code is changed and rendered again, and the whole chapter is updated.
19.8 Choosing a method
The choice of method for a new research question depends on the goal, on the kind of outcome, and on how the observations are related. Figure 19.3 summarises the methods of this book as a guide.
%%{init: {"flowchart": {"nodeSpacing": 18, "rankSpacing": 45}}}%%
flowchart TD
Q{What is the goal?} -->|Describe| D[Summaries and plots<br/>Ch 4, 6]
Q -->|Explain or compare| O{Outcome type?}
Q -->|Predict new cases| P[Machine learning<br/>Ch 11-15]
Q -->|Find groups or<br/>structure| U[PCA, factor analysis,<br/>clustering<br/>Ch 9, 14]
Q -->|Forecast over time| T[Time series<br/>Ch 16]
O -->|Numeric| N{Repeated or<br/>nested data?}
O -->|Yes / No| L[Chi-square, logistic<br/>regression<br/>Ch 7-8]
N -->|No| R[t-test, ANOVA,<br/>regression<br/>Ch 7-8]
N -->|Yes| M[Mixed-effects<br/>models<br/>Ch 10]
Table 19.3 shows how each of the study’s research questions was answered.
| Research question | Method | Chapters |
|---|---|---|
| RQ1 What does graduate life look like? | Plots, descriptive statistics | 4, 6 |
| RQ2 Do students sleep less than 7 hours? | One-sample t-test, confidence interval | 7 |
| RQ3 Does the workshop improve wellbeing? | Two-sample t-test; mixed-effects model | 7, 10 |
| RQ4 Do faculties and study modes differ? | ANOVA, post-hoc tests | 7, 8 |
| RQ5 What explains GPA? | Multiple regression | 8, 13 |
| RQ6 Does the questionnaire measure what it should? | Factor analysis, Cronbach’s alpha | 9 |
| RQ7 Are there student profiles? | Cluster analysis, mixture models | 9, 14 |
| RQ8 How do wellbeing and GPA change? | Mixed-effects models | 10 |
| RQ9 Who considers dropping out? | Logistic regression; classification | 8, 11, 12, 15 |
| RQ10 Can final GPA be predicted? | Regularised regression, gradient boosting | 13 |
| RQ11 What challenges do students describe? | Text coding with a language model | 18 |
| RQ12 How many counselling visits next year? | Time series forecasting | 16 |
19.9 Challenges every researcher meets
Real projects rarely go as planned, and some problems are almost universal. Every real dataset is messy and needs cleaning, which takes longer than the analysis; it is done in code, checked after every step, and never applied to the raw file itself (Chapter 3). Missing data raises the question of why it is missing before anything is done about it, because dropping incomplete cases can bias results when the missing data is not random, as with the students who left the programme (Chapter 6). Small samples give wide confidence intervals and low power (Chapter 7) and make complex models overfit (Chapter 13); the remedies are to report effect sizes with intervals, keep models simple, and plan the sample size before collecting data with a power analysis (Chapter 5).
Analysis brings its own problems. Many tests produce many false alarms, so the main analyses are decided in advance, multiple comparisons are corrected where needed, and exploratory results are reported as exploratory (Chapters 7, 8, and 17). Assumptions are checked with plots rather than with tests alone, with robust tests, non-parametric tests, transformations, and mixed models as the alternatives (Chapters 7, 8, and 10). Correlation is not causation: observational data rarely proves causes, so confounders must be thought through, as sleep was behind the caffeine effect, and randomised designs used where possible, as in the workshop study (Chapters 5 and 8). Prediction and explanation are different aims: a model that predicts well may explain little, and the reverse (Chapter 11). Finally, results must be communicated to supervisors, co-authors, and examiners who may use other software; a clear results chapter with tables, figures, and an appendix of code serves them all, and data can be exported with to_csv() or pyreadstat when they need it (Chapter 2).
19.10 Where to go next
This book is a beginning. Depending on your field, the next methods to learn may be:
| Topic | What it is for | Python Packages |
|---|---|---|
| Structural equation modelling | Confirmatory factor analysis, path models, latent variables | semopy |
| Bayesian statistics | Models with prior knowledge and full uncertainty | PyMC, bambi |
| Survival analysis | Time until an event, such as dropping out | lifelines |
| Meta-analysis | Combining results from several studies | statsmodels |
| Natural language processing | Words, sentiment, and embeddings in text | spacy, transformers |
| Spatial data | Maps and geographic data | geopandas |
| Publication tables | Formatted summary tables of results | great_tables, tabulate |
| Interpreting models | Feature importances, SHAP values, and partial effects | shap, scikit-learn |
Some of the best resources are free online: Python for Data Analysis by Wes McKinney (McKinney 2022), Regression and Other Stories (Gelman et al. 2020) for regression modeling, An Introduction to Statistical Learning with Applications in Python (James et al. 2023) for machine learning, and the official documentation of Scikit-Learn, Statsmodels, and PyTorch. Appendix E lists more.
You do not have to learn alone. The Python data science community is large and welcoming: Stack Overflow, Python Discord servers, local PyData meetups, and open-source contributors around the world. Asking a clear question, with a small reproducible example, is itself an invaluable research skill, and the same skill that makes an AI assistant useful (Chapter 18).
19.11 Common misconceptions
A few misunderstandings are common at the stage of writing up.
- “A significant result answers the research question.” It answers one link of the chain; the design and the measures decide what the result means.
- “The discussion should defend the results.” It should also state their limits; examiners trust a thesis more when it says clearly what it cannot show.
- “Formatting numbers by hand is quicker.” It is quicker once, and wrong the next time the data or the model changes.
- “Choosing a method means choosing the most advanced one.” The right method is the simplest one that matches the goal, the outcome, and the structure of the data.
19.12 Chapter review
19.12.1 Summary
- A research project follows a path from question and design through import, cleaning, exploration, and modelling to reporting and sharing, often going back to earlier steps. Writing every step in code makes going back easy.
- Organise projects with read-only raw data, numbered scripts, relative paths, a README, and a Quarto results document; everything else can be recreated.
- Check the cleaned data at the end of the cleaning script, then describe the sample, answer each research question with the method suited to it, and report results with numbers taken directly from the code.
- Every result rests on a chain of reasoning, from question and hypothesis through design, variables, and analysis to result, conclusion, and limitation. Reporting standards such as the APA’s JARS, CONSORT, and STROBE list what a report must contain.
- Small helper functions (for formatting means, percentages, and p-values) avoid copying and pasting code; pandas and statsmodels turn model results into tidy summary tables; matplotlib subplots combine figures.
- Choose methods by the goal (describe, explain, predict, find structure, forecast), the type of outcome, and how the observations are related.
- Messy and missing data, small samples, many tests, assumptions, and causation are challenges in every project; the chapters of this book give tools for each.
19.12.2 Key terms
Research workflow, project structure, raw data, pipeline, sample description (“Table 1”), helper function, tidy model summary, multi-panel figure, reporting sentence, chain of reasoning, reporting standard, method choice.
19.13 Exercises
The playground has these and more, and the downloadable project contains the complete thesis project.
- Download the Chapter 19 project, run
scripts/01_clean_data.pyandscripts/02_analysis.py, and renderresults.qmd. Then change one cleaning rule (for example, treat ages above 70 as impossible), render again, and note which numbers change. - Add a row to the sample table (Table 19.1) for the share of students with children.
- Write a reporting sentence, with inline numbers, for the effect of support on GPA in the regression model.
- Add a third panel to the thesis figure: wellbeing by faculty as a box plot.
- Choose a research question from your own field. Using Figure 19.3, decide which method you would use, and which chapter of this book you would reread first.
- Complete the chain of reasoning (Table 19.2) for RQ2, whether students sleep less than 7 hours, using the results of Chapter 7.
19.14 Further reading
- Python for Data Analysis (McKinney 2022) covers data manipulation, reshaping, and exploratory analysis in Python.
- Regression and Other Stories (Gelman et al. 2020) is an excellent guide to building, checking, and reporting regression models in real research.
- An Introduction to Statistical Learning with Applications in Python (James et al. 2023) provides hands-on machine learning modeling.