Run a diary study from question to synthesis: define the research question and logging period, recruit and brief participants, design entry prompts and a logging template, set a reminder and compliance routine, handle missed entries honestly, and synthesize entries into themes with counts. Use when behavior or experience needs to be captured over time in the participant setting, as it happens, rather than recalled later in a single session.
name: diary_study_basics description: "Run a diary study from question to synthesis: define the research question and logging period, recruit and brief participants, design entry prompts and a logging template, set a reminder and compliance routine, handle missed entries honestly, and synthesize entries into themes with counts. Use when behavior or experience needs to be captured over time in the participant setting, as it happens, rather than recalled later in a single session." metadata: { "includeInPrompt": true }
Diary Study Basics
Purpose
Run a diary study that ends in a defensible synthesis: a compliance record for the whole logging period and a theme table built from the entries that were actually received. Participants log short entries in their own setting, close to the moment an event happens, across a set period. The pattern across days and across participants is the evidence. Single entries are context for that pattern, not findings on their own.
Scope:
- survey-design-basics covers one-shot quantitative surveys: a fixed questionnaire answered once, with results read as response counts and scale distributions. This skill covers repeated self-report over a logging period, where timing, missing days, and change over time are part of the data. A diary study does not produce survey scale scores.
- customer-interviews covers live discovery interviews: a researcher asks questions in a session and probes answers in the moment. This skill has no live session during logging. Participants write or record entries on their own, and the researcher contact during the period is limited to reminders, technical help, and compliance checks.
- interview-transcript-analysis covers coding transcripts after collection: it builds a codebook and applies it to recorded talk from interviews or focus groups. This skill covers design and collection for diary entries, plus a first synthesis of those entries into themes with counts. If longer debrief interviews are recorded after the logging period, coding those transcripts belongs to that skill.
- This skill does not cover clinical diaries, medical symptom tracking, or studies that require validated instruments. It ends at a documented compliance record and theme synthesis that other research can follow up.
- Verification note: the tally script in the Operating Rules section uses only the Python standard library and was run on 2026-10-16 under Python 3.12.3 against its built-in fixture. Every number quoted in this skill comes from that run. No diary platform is named with feature claims, because tools differ in what they export: any tool or form that exports, per entry, the participant id, the day, the entry text, and a submission time can supply the input this skill needs. Confirm the export shape before recruiting, not after the period closes.
Workflow
- Define the research question and the logging period.
- Write the research question in one sentence, in terms of behavior or experience over time: for example, how a routine changes across a work week, when a friction point occurs during a repeated task, or what happens before and after a defined event.
- Check that the question needs repeated capture. If a single session could answer it from recall, use customer-interviews or survey-design-basics instead. Diary studies earn their cost when recall would be weak, the event is spread across days, or the setting matters.
- Set the logging period from the cycle of the behavior, not from convenience. A weekly routine needs a period that covers the full weekly cycle. An event-driven question needs a period that gives the event a fair chance to occur for most participants.
- Record the start day, the end day, and the time zone rule for what counts as a given day. Entries near midnight are common, and the day assignment rule must be fixed before logging starts.
- Write down what a usable result looks like before recruiting: a compliance level that still supports reading patterns by day, and a minimum entry count per participant for inclusion in the theme synthesis. Fix both values in the study brief so later decisions are not fitted to the data that arrived.
- Decide the entry design: scheduled, event-based, or combined.
- Scheduled entries: participants log at a set time or at a set interval, for example at the end of each day. Use this design when the question is about a routine, a day-level pattern, or comparison across days.
- Event-based entries: participants log each time a defined event occurs. Use this design when the question is about the event itself: what led to it, what happened, and what followed. Define the event in plain words a participant can judge in the moment, and state what to do when the event happens many times in one day.
- Combined design: a scheduled entry that asks whether the defined event occurred, plus an event entry when it did. Use this design when absence matters and a missing event entry alone cannot show whether nothing happened or logging was skipped.
- Pick one design per study and record why. Mixing designs mid-study splits the data into two studies with different denominators.
- Set entries per day to match the design. The tally script assumes one scheduled slot per participant per day. If the design calls for more slots per day, change entries_per_day in the script and keep the same expected entry math.
- Recruit and brief participants.
- Recruit from the population named in step 1, screened on the behavior under study: for example, has done the task in a recent period, follows the routine, or expects the defined event during the logging period. Screening on general demographics alone does not predict whether useful entries will arrive.
- State the full commitment at recruitment: the logging period, the entry frequency, the expected time per entry, the reminder routine, and how payment or incentive relates to completion. Participants who learn the commitment after joining drop out during the period, when replacement is impossible.
- Over-recruit against dropout and low compliance, and record the target recruited count, the target completer count, and the inclusion rule from step 1 in the study brief.
- Brief each participant before day one with a short written guide and a practice entry. The guide covers: what to log, when to log it, how detailed an entry should be, what to do after a missed slot, and who to contact with a problem. The practice entry uses material outside the study data and confirms the participant can submit in the chosen format.
- Collect consent for the content the study will hold. Diary entries can include details about other people, locations, and work matters that a session study would never capture. State what participants should leave out, and how entries will be stored, read, and quoted.
- Confirm the reminder channel with each participant during briefing. A reminder sent to a channel the participant rarely checks is a design defect, not a participant failure.
- Design entry prompts and the logging template.
- Write prompts that ask for concrete recall close to the moment: what happened, when it happened, where it happened, who was involved in general terms, what the participant did, and what made the moment easy or hard. Prompts that ask for general opinions produce essay entries that cannot be compared across days.
- Keep the required core small and fixed. Every entry answers the same core prompts, in the same wording, for the whole period. Changing prompt wording mid-study breaks comparison across the days before and after the change.
- Add optional prompts with care. One rotating optional prompt per day can cover secondary questions without lengthening every entry. Record which optional prompt ran on which day, because answers cannot be compared across days that asked different questions.
- Build the logging template as a form with named fields, not a blank page. A usable template has: participant id, day, submission time, one field per core prompt, a field for event occurrence in a combined design, and a flag field for whether the entry was written late. Field names in the template become field names in the export.
- Pilot the template with a small number of participants from the target population. Time one entry, read the pilot entries for prompt wording that was read two ways, and fix wording problems before the main period starts. Freeze the template after the pilot and log every later change with its date.
- Give each prompt a worked example in the briefing guide, using invented material that cannot be mistaken for study data. Examples set entry length and detail level more reliably than length instructions alone.
- Set the reminder and compliance routine.
- Schedule reminders to match the entry design: a reminder near the scheduled logging time for scheduled entries, and a single end-of-day check prompt for event-based entries. State the reminder time, the channel, and the sender in the study brief.
- Limit reminders during the period. Reminders support logging, and repeated extra messages train participants to ignore the channel. Any extra check-in message beyond the schedule is logged with its date and reason.
- Check compliance daily while the study is open. Run the tally from the Operating Rules section on the export received so far, read expected against received per participant, and act the same day: a technical problem found early can be fixed, while the same problem found after the period closes cannot.
- Use a fixed contact rule for low compliance, written before the period starts: for example, contact a participant after a set number of missed slots, ask whether a problem exists, offer technical help, and record the contact and the reply. Do not coach entry content during compliance contact.
- Keep the raw export untouched. Save each daily export as delivered, with the study date and the schedule version, before any cleaning begins.
- Run the logging period and watch data quality.
- Read the first entries on the day they arrive. Check entry length, prompt coverage, and day assignment. A template defect found on day one affects one day of data. The same defect found at the end affects the whole period.
- Flag quality problems by rules written before results are read: for example, entries far shorter than the pilot range, entries that answer none of the core prompts, or entries submitted in a batch long after the days they describe. Flagging is a record. Step 7 decides how flagged entries are handled, and the report states the decision.
- Record context events that affect all participants during the period, such as a holiday, a service outage, or a schedule change in the setting under study. Day patterns cannot be read fairly without that record.
- Do not extend the period for some participants and not others without logging it as a schedule change. Different period lengths change the expected entry denominator per participant, and the tally must use the schedule each participant actually had.
- Handle missed entries honestly.
- Count missed entries as missed. Expected entries come from the schedule and the participant count. Received entries come from the export. The difference is the missed count, and it stays in every compliance figure the report quotes.
- Allow backfilled entries only under a rule fixed before the period starts: for example, an entry written the next morning for the prior day, marked with the backfilled flag in the template. Backfilled entries are counted as received, reported as their own count, and read with the lateness noted. In the fixture, backfilled entries are 2 of the 16 received entries.
- Never ask a participant to reconstruct a full set of missed days from memory at the end of the period. Reconstructed entries measure recall at the end, not experience on the day, and they hide the compliance problem the study needs to report.
- Apply the inclusion rule from step 1 at close, and log every excluded participant with the reason. Report compliance twice when exclusions happen: compliance across all recruited participants, and compliance across included participants only. The first figure describes the study as run. The second describes the data the synthesis rests on.
- Report dropout plainly: who stopped logging, on which day logging stopped, and whether a reason was given. A study with clean theme counts and hidden dropout overstates its evidence.
- Close the period and prepare entries for synthesis.
- Close logging at the stated end time, export the full data set, and save the raw export before cleaning. Record the final schedule, the final participant list, and any schedule changes from step 6.
- Clean on a copy. Standardize participant ids, confirm day assignment under the rule from step 1, and confirm each received entry sits in a scheduled slot. Entries outside any slot are logged and handled under a written rule, not silently dropped or silently counted.
- De-identify entries before synthesis if the report will quote them. Replace names, locations, and account details with bracketed labels, and keep a log that maps each replacement to its entry id so quotes can be checked against the raw entry.
- Run compliance_tally.py on the cleaned data in the same shape as the fixture and save the full output. This output is the source for every compliance and theme count in the report.
- Synthesize entries into themes with counts.
- Read a sample of entries across participants and across days before naming any theme. Themes built from the first entries alone tend to fit early days and miss patterns that appear later in the period.
- Write a theme list with a definition and an inclusion rule per theme. A theme definition states what an entry must contain to receive the theme. A theme is applied per entry: one entry can carry more than one theme, and an entry can carry none. Record themes as a list on each entry, in the shape the tally script expects.
- Apply the theme list to all received entries, entry by entry, in a fixed order. When a theme definition changes during coding, recode every entry coded under the old definition and log the change with its date. Partial recoding leaves two definitions mixed in one count.
- Count with compliance_tally.py, not by hand. In the fixture, the theme counts are commute 9, app-friction 6, positive-moment 6, and work-meeting 6, across 16 received entries, with 4 distinct themes. Counts state how many received entries carry each theme. Because one entry can carry more than one theme, theme counts do not sum to the received entry count, and the report must say so.
- Read themes against compliance and against time. A theme carried mainly by one high-compliance participant describes that participant, not the group. In the fixture, P03 received 2 of 5 expected entries at 40.0 percent compliance, so any theme resting mainly on P03 entries carries that limit. A theme that appears only in early days and a theme that grows across the period are different findings, and the synthesis should separate them by day before merging them.
- Keep counter-examples visible. For each major theme, note entries that sit close to the theme definition and were not coded with it, plus any received entry coded with no theme. A synthesis that reports only supporting entries overstates how clean the pattern is.
- State every theme count with its denominator, as a count over received entries. In the fixture, commute at 9 means 9 of 16 received entries. A count quoted without the received entry denominator is a defect in the report.
- Report the findings and the limits together.
- Lead with compliance, then themes. A reader who sees theme counts before the compliance record cannot judge what the counts rest on. In the fixture, the headline record is expected 20, received 16, missed 4, and overall compliance 80.0 percent.
- Give each theme its count, its definition, the days it appeared on, and the spread of participants who contributed entries to it. Themes with a narrow participant spread are reported as narrow, not as group patterns.
- Quote entries sparingly and with entry ids, after de-identification. A quote illustrates a theme already supported by the count. A quote does not replace the count.
- Close with limits: dropout, missed entry patterns by day, backfilled share, prompt or template changes during the period, and context events from step 6. Name the follow-up that would settle each open point, such as a debrief interview round coded under interview-transcript-analysis, or a one-shot survey under survey-design-basics to size a theme in a wider group.
Output Contract
Deliver a diary study report with these sections, in this order:
- Study brief: the research question (Workflow step 1), the entry design chosen and why, the logging period and day assignment rule, the schedule (study days and entries per day), the recruited and included participant counts, and the inclusion rule fixed before logging started.
- Materials record: the exact entry prompts as run, the logging template fields, the briefing guide version, the reminder schedule, and every prompt, template, or schedule change made after the pilot, with dates.
- Compliance record: the full output of compliance_tally.py on the cleaned data. On the worked fixture in the Operating Rules section the headline numbers are: participants 4, study days 5, entries per day 1, expected per participant 5, expected total 20, received total 16, missed total 4, overall compliance 80.0 percent, backfilled entries 2, per participant compliance P01 100.0 percent, P02 80.0 percent, P03 40.0 percent, and P04 100.0 percent.
- Data record: raw exports saved untouched, cleaning steps applied with reasons, entries outside scheduled slots and how they were handled, excluded participants with reasons, and the de-identification log if entries are quoted.
- Theme record: the theme list with a definition and inclusion rule per theme, the coding log (including any definition change and the recoding it required), and the theme counts from compliance_tally.py stated as counts over received entries. On the fixture the counts are commute 9, app-friction 6, positive-moment 6, and work-meeting 6, with 4 distinct themes across 16 received entries.
- Findings: one entry per theme with its count and denominator, the days it appeared on, the participants who contributed to it, and its counter-examples from Workflow step 9.
- Limits and follow-ups: dropout by day, missed entries by day, backfilled share, context events during the period, themes resting on a narrow participant spread, and the follow-up study that would settle each open point.
Operating Rules
- Raw data stays raw. Each export as delivered is saved before cleaning, cleaning is applied to a copy, and every applied change is logged with its reason. A synthesis nobody can trace back to the raw entries is not evidence.
- Expected entries come from the schedule only. Expected total is participants multiplied by study days multiplied by entries per day. Changing the schedule changes the denominator, and the change is logged and reported. Received and missed counts are never adjusted to make compliance look better.
- Missed stays missed. A slot with no entry is a missed entry in every figure. Backfilled entries are flagged, counted separately, and reported with their count. Reconstructed entries written long after the day they describe are not counted as received.
- Counts before percentages. Every compliance figure is reported as a count first, with the percentage after it, and every theme count is reported as a count over received entries. A percentage quoted without its counts is a defect in the report.
- One theme list, applied to all entries. Theme definitions are written before full coding starts, every definition change triggers a full recode of entries coded under the old definition, and the coding log ships with the report.
- Themes are counted per entry, not per mention. An entry that mentions a theme topic multiple times receives the theme once. This keeps theme counts as entry counts that share the received entry denominator.
- No coaching during logging. Researcher contact during the period is limited to the reminder schedule, technical help, and the fixed low-compliance contact rule. Entry content is never suggested, corrected, or praised in a way that steers later entries.
- Quote numbers only from the tally output. Compliance figures, missed counts, backfilled counts, and theme counts come from compliance_tally.py output, never from memory of browsing the entries. If a needed number is not in the output, extend the script and rerun it rather than estimating.
- Use the tally script below for compliance and theme counts. Save it as compliance_tally.py, run it on the fixture to confirm your copy works, then replace the SCHEDULE, PARTICIPANTS, and FIXTURE_ENTRIES data with the study data in the same shape and rerun it. Report numbers from its output, not from memory of the entries.
Diary study tally (compliance_tally.py)
Run: python3 compliance_tally.py prints the full tally of the built-in fixture. Run: python3 compliance_tally.py --self-test prints the same tally, checks the headline counts, confirms that an entry from an unknown participant is rejected, and ends with self_test: PASS (bad fixture rejected).
Expected output for the fixture run:
text
participants: 4
study_days: 5
entries_per_day: 1
expected_per_participant: 5
expected_total: 20
received_total: 16
missed_total: 4
overall_compliance_pct: 80.0
backfilled_entries: 2
per_participant:
P01: expected 5, received 5, missed 0, compliance 100.0%
P02: expected 5, received 4, missed 1, compliance 80.0%
P03: expected 5, received 2, missed 3, compliance 40.0%
P04: expected 5, received 5, missed 0, compliance 100.0%
theme_counts:
commute: 9
app-friction: 6
positive-moment: 6
work-meeting: 6
distinct_themes: 4
The fixture is a teaching example with 4 participants and a 5 day period at 1 entry per day, built so every reading in Workflow steps 7 and 9 has a case: a full-compliance pair (P01 and P04, each at 100.0 percent), a mid-compliance participant (P02 at 80.0 percent, with 1 missed entry and 1 backfilled entry among the 2 backfilled entries in the study), a low-compliance participant (P03 at 40.0 percent, with 3 missed entries), and a theme spread where one theme leads (commute at 9) while the other three themes tie (app-friction, positive-moment, and work-meeting, each at 6). Overall compliance is 80.0 percent from 16 received of 20 expected, with 4 missed.
compliance_tally.py (Python 3, standard library only). Save this code as compliance_tally.py:
python
#!/usr/bin/env python3
"""Tally diary study compliance and entry themes.
Uses only the Python standard library. No external data file is needed:
the worked fixture is built in below, and tally() accepts the same
shape for real data.
Data shape:
schedule: dict with study_days (int) and entries_per_day (int)
participants: list of participant ids (strings)
entries: list of dicts, each with participant, day, themes (list of
strings), and backfilled (bool). One entry per participant per
scheduled slot. A day with entries_per_day 1 has one slot per day.
Run:
python3 compliance_tally.py
python3 compliance_tally.py --self-test
What it reports: expected entries, received entries, missed entries,
overall compliance as a count and a percentage, the same figures per
participant, backfilled count, and theme counts across received
entries sorted by count then name.
"""
import sys
from collections import Counter
SCHEDULE = {"study_days": 5, "entries_per_day": 1}
PARTICIPANTS = ["P01", "P02", "P03", "P04"]
# Each dict is one received entry. Missing slots are absent on purpose:
# P02 has no day 4 entry, P03 has no entries on days 3, 4, or 5.
FIXTURE_ENTRIES = [
{"participant": "P01", "day": 1, "themes": ["commute", "app-friction"], "backfilled": False},
{"participant": "P01", "day": 2, "themes": ["commute", "positive-moment"], "backfilled": False},
{"participant": "P01", "day": 3, "themes": ["work-meeting", "app-friction"], "backfilled": False},
{"participant": "P01", "day": 4, "themes": ["commute"], "backfilled": False},
{"participant": "P01", "day": 5, "themes": ["work-meeting", "positive-moment"], "backfilled": False},
{"participant": "P02", "day": 1, "themes": ["commute", "positive-moment"], "backfilled": False},
{"participant": "P02", "day": 2, "themes": ["app-friction"], "backfilled": False},
{"participant": "P02", "day": 3, "themes": ["commute", "work-meeting"], "backfilled": False},
{"participant": "P02", "day": 5, "themes": ["positive-moment"], "backfilled": True},
{"participant": "P03", "day": 1, "themes": ["work-meeting"], "backfilled": False},
{"participant": "P03", "day": 2, "themes": ["app-friction", "commute"], "backfilled": False},
{"participant": "P04", "day": 1, "themes": ["positive-moment", "commute"], "backfilled": False},
{"participant": "P04", "day": 2, "themes": ["work-meeting", "app-friction"], "backfilled": False},
{"participant": "P04", "day": 3, "themes": ["commute", "positive-moment"], "backfilled": False},
{"participant": "P04", "day": 4, "themes": ["app-friction"], "backfilled": False},
{"participant": "P04", "day": 5, "themes": ["commute", "work-meeting"], "backfilled": True},
]
def validate(schedule, participants, entries):
days = schedule["study_days"]
per_day = schedule["entries_per_day"]
if days < 1 or per_day < 1:
raise ValueError("schedule values must be at least 1")
known = set(participants)
seen = Counter()
for e in entries:
pid = e["participant"]
if pid not in known:
raise ValueError(f"unknown participant: {pid}")
if not (1 <= e["day"] <= days):
raise ValueError(f"day out of range for {pid}: {e['day']}")
seen[(pid, e["day"])] += 1
if seen[(pid, e["day"])] > per_day:
raise ValueError(f"too many entries for {pid} on day {e['day']}")
# An entry with an empty themes list is valid: it was received
# and coded with no theme (see Workflow step 9).
def tally(schedule, participants, entries):
validate(schedule, participants, entries)
expected_per = schedule["study_days"] * schedule["entries_per_day"]
expected_total = expected_per * len(participants)
received_total = len(entries)
missed_total = expected_total - received_total
compliance = received_total / expected_total * 100 if expected_total else 0.0
backfilled = sum(1 for e in entries if e["backfilled"])
by_participant = {}
for pid in participants:
received = sum(1 for e in entries if e["participant"] == pid)
missed = expected_per - received
rate = received / expected_per * 100 if expected_per else 0.0
by_participant[pid] = (expected_per, received, missed, rate)
themes = Counter()
for e in entries:
for theme in e["themes"]:
themes[theme] += 1
print(f"participants: {len(participants)}")
print(f"study_days: {schedule['study_days']}")
print(f"entries_per_day: {schedule['entries_per_day']}")
print(f"expected_per_participant: {expected_per}")
print(f"expected_total: {expected_total}")
print(f"received_total: {received_total}")
print(f"missed_total: {missed_total}")
print(f"overall_compliance_pct: {compliance:.1f}")
print(f"backfilled_entries: {backfilled}")
print("per_participant:")
for pid in sorted(by_participant):
exp, rec, miss, rate = by_participant[pid]
print(f" {pid}: expected {exp}, received {rec}, missed {miss}, compliance {rate:.1f}%")
print("theme_counts:")
for theme, count in sorted(themes.items(), key=lambda kv: (-kv[1], kv[0])):
print(f" {theme}: {count}")
print(f"distinct_themes: {len(themes)}")
return {
"participants": len(participants),
"expected_total": expected_total,
"received_total": received_total,
"missed_total": missed_total,
"overall_compliance_pct": round(compliance, 1),
"backfilled_entries": backfilled,
"distinct_themes": len(themes),
"theme_counts": dict(themes),
"per_participant": by_participant,
}
def self_test():
result = tally(SCHEDULE, PARTICIPANTS, FIXTURE_ENTRIES)
assert result["participants"] == 4, result
assert result["expected_total"] == 20, result
assert result["received_total"] == 16, result
assert result["missed_total"] == 4, result
assert result["overall_compliance_pct"] == 80.0, result
assert result["backfilled_entries"] == 2, result
assert result["distinct_themes"] == 4, result
assert result["theme_counts"] == {
"commute": 9,
"app-friction": 6,
"positive-moment": 6,
"work-meeting": 6,
}, result
bad = [{"participant": "P99", "day": 1, "themes": ["commute"], "backfilled": False}]
try:
validate(SCHEDULE, PARTICIPANTS, bad)
except ValueError:
print("self_test: PASS (bad fixture rejected)")
return 0
print("self_test: FAIL (bad fixture accepted)")
return 1
def main():
args = sys.argv[1:]
if "--self-test" in args:
return self_test()
tally(SCHEDULE, PARTICIPANTS, FIXTURE_ENTRIES)
return 0
if __name__ == "__main__":
raise SystemExit(main())
No ratings yet — be the first to rate it.