# ============================================================================= # HSCI 841 Qualitative Research Methods and Analysis in Public Health - Lesson 4: # Qualitative Data Collection # Answer key for the in-lesson R activities # Data file(s): the 20 transcripts in term projects/HSCI_841/transcripts/ # (P01_Maya.txt ... P20_Frank.txt; all of them are in HSCI_841_loneliness_data.zip) # Packages: readtext, quanteda, dplyr (install once with # install.packages(c("readtext", "quanteda", "dplyr"))) # Reproduces every code block in the lesson. # # Paths are relative to the course repository root. Set your working directory there # (Session > Set Working Directory > Choose Directory, or open the course .Rproj) before running. # ============================================================================= # NOTE (answer key): the transcripts contain typographic dashes and curly quotation marks. If the R # session is not running in a UTF-8 locale (plain Rscript on a server, for example), those characters # are escaped rather than read, which pollutes word-frequency output. RStudio on Windows, macOS and # Linux is already UTF-8; this line only matters for a bare Rscript run. if (!isTRUE(l10n_info()[["UTF-8"]])) invisible(suppressWarnings(Sys.setlocale("LC_CTYPE", "C.UTF-8"))) # ==== Section 3: Reading the Corpus into R / Read the transcript corpus and summarise word counts ==== # Read all 20 loneliness transcripts as a corpus library(readtext) library(quanteda) library(dplyr) # NOTE (answer key): path corrected from "../term projects/..." to the repository-root convention. transcript_dir <- "term projects/HSCI_841/transcripts" # readtext::readtext() reads every .txt file in a folder into a tibble # with one row per file, a doc_id column, and a text column loneliness_texts <- readtext(file.path(transcript_dir, "*.txt"), docvarsfrom = "filenames", dvsep = "_", docvarnames = c("pid", "pseudonym")) # Turn it into a quanteda corpus object loneliness_corpus <- corpus(loneliness_texts) # Words per transcript word_counts <- ntoken(loneliness_corpus, remove_punct = TRUE) # Build a tidy summary summary_df <- tibble( pid = docvars(loneliness_corpus, "pid"), pseudonym = docvars(loneliness_corpus, "pseudonym"), words = as.integer(word_counts) ) %>% arrange(desc(words)) print(summary_df, n = 20) summary(summary_df$words) # Median, IQR, min, max give a sense of the spread in transcript length # ---- Reading the summary ---- cat("Documents:", ndoc(loneliness_corpus), " total words:", sum(summary_df$words), "\n") cat("Longest:", summary_df$pseudonym[1], summary_df$words[1], "words;", "shortest:", summary_df$pseudonym[nrow(summary_df)], summary_df$words[nrow(summary_df)], "words\n") cat("Median", median(summary_df$words), "words, IQR", paste(quantile(summary_df$words, c(0.25, 0.75)), collapse = " to "), "\n") # The spread is a data-collection finding, not a nuisance: the shortest interviews are with the # youngest participant (P12 Tyler) and with P10 Daniel, who was guarded about a topic he has not # discussed with his family. Interview length is itself evidence about rapport and elicitation. # Interviewer and participant talk, separately: a simple check on who is doing the talking. turn_counts <- lapply(list.files(transcript_dir, pattern = "\\.txt$", full.names = TRUE), function(f) { lines <- readLines(f, warn = FALSE) data.frame(file = basename(f), interviewer_turns = sum(grepl("^I:", lines)), participant_turns = sum(grepl("^P( \\(continued\\))?:", lines)), participant_words = sum(lengths(strsplit(grep("^P( \\(continued\\))?:", lines, value = TRUE), "\\s+")))) }) turn_counts <- bind_rows(turn_counts) print(turn_counts) cat("Participant words as a share of all words:", round(100 * sum(turn_counts$participant_words) / sum(summary_df$words)), "%\n") # A guide that works keeps the interviewer's share small. Here the participants do most of the # talking, which is what a semi-structured guide is for.