Skip to contents

Turns rows of a persona data frame (one respondent per row, demographics plus survey or attitude answers) into a silicon_panel whose personas can be administered survey items. It is built for frames following the LLMR persona contract, such as the bundled anes_2024_personas: the demographic fields are those that LLMR::llm_persona_demographic_fields() names, every other column is a stated answer, answers are shown under their question wording when the frame carries a dictionary or questions gives one, and each persona is rendered as a person to answer in character ("You are this person: ... On a questionnaire you gave these answers: ...").

Usage

panel_from_personas(
  data,
  n = NULL,
  rows = NULL,
  weights = NULL,
  questions = NULL,
  na_strings = NULL
)

Arguments

data

A persona data frame, such as anes_2024_personas. Those 100 rows are diversity-selected United States respondents and carry no survey weights, so they stand for no population.

n

Optional panel size. With NULL, every selected row is used; with a number, rows are sampled (without replacement when n does not exceed the pool, otherwise with replacement).

rows

Optional row selector: a numeric vector of whole-number row positions, a logical vector with one entry per row, or a predicate function(df) returning such a logical vector. Applied before sampling. Character and factor selectors are refused, since a factor's codes are not row positions.

weights

Optional survey weights for the draw: a column name in data, or a numeric vector aligned to the selected rows. Used only when n is given. NULL (default) draws uniformly. A weight column is never rendered into the persona text.

questions

Optional named character vector of question texts keyed by column name, as in c(services = "Should the government provide fewer or more services?"). It takes precedence over the question in the frame's dictionary. Names that are not columns of data draw a warning.

na_strings

Regular expressions, matched ignoring case against each value and value label, that mark it as missing. NULL (the default) uses the patterns of FocusGroup's fg_default_na_strings(), which match the missing-data labels of the ANES and similar releases: "Inapplicable", "Refused", "Don't know", "Not asked", "DK/RF", "No post-election interview", "Insufficient partial, interview deleted", "RESTRICTED", "Error", "Technical error", "Not reported", and the like. character(0) treats no label as missing. Answers that use such words ("2. Refused to take sides", "I keep missing the bus") are kept.

Value

A silicon_panel: a tibble with persona_id, the demographic columns as the persona text gives them (NA where a value is left out), and persona, with the attributes margins, source, and source_rows.

Details

Unlike panel_from_margins() and panel_from_data(), this constructor keeps each selected respondent's answers together. The margins attribute contains the demographic distribution of the selected rows, and the source_rows attribute gives, for each persona in persona_id order, the row of data it was drawn from. A persona can therefore be traced to its source record with attr(panel, "source_rows")[panel$persona_id], which still holds after the panel's rows are subset.

For a reproducible draw, set a seed before calling (the function never sets one itself).

Rendering of values

Each value of a rendered field is written as text by these rules.

  • A labeled column, such as haven::read_dta() and haven::read_sav() return, is decoded from its labels attribute; haven itself is not needed. A labeled code is shown as its label without the code prefix ("1. Male" becomes "Male"). A code without a label on a scale labeled only at its ends is shown with the scale, as in "5 on a scale from 1 (Fewer services) to 7 (More services)"; on a scale centered on zero the ends are the labeled codes -m and m, even when its midpoint is labeled too. A negative code without a label is left out as missing, with a warning. Any other code without a label is shown as the code, with a warning, unless the column labels at most one nonnegative code or only top and bottom codes ("80. Age 80 or older"), in which case the column holds numbers and the value is shown as one. Values that an SPSS file declares missing (na_values, na_range) are left out.

  • A factor's levels are its labels. Levels as haven::as_factor() writes them ("1. Fewer services", "[1] 1. Fewer services", and bare numbers for unlabeled codes) are decoded by the rules above; a level without a code prefix ("Refused" from a file whose labels carry none) counts as a label whose code is unknown. A character column that mixes coded labels with bare numbers, as as.character(haven::as_factor()) writes it, is decoded the same way. Code prefixes are removed from factor levels and from character values.

  • NA, empty values, and missing-data labels are left out. A value or a value label is one when it matches na_strings and either carries a missing-data code (negative, or 90 and above: "-9 Refused", "998. Don't know") or says nothing but the matched wording once codes, interviewer markers, and asides in parentheses are set aside ("Refused to answer", "Don't know (VOL)"), as FocusGroup decides, so a free-text answer such as "I refused to answer the door" is kept. A value with a negative code prefix that does not match na_strings is shown, with a warning, since many releases use negative codes for missing data; the labels of negative codes on a scale centered on zero ("-3. Far left" beside "3. Far right") draw none.

  • Numbers are written in full: 100000, not 1e+05.

  • A label exactly 80 characters long draws a warning. Stata files cut value labels at 80 characters, and the ANES 2024 release has labels cut there.

  • Identifier columns (named, ignoring case, id, caseid, case_id, respondent_id, respid, resp_id, persona_id, uid, record_id, or row_id, or with a variable label that calls them a case or respondent ID, as the ANES label of V240001 does), the column named by weights, a column named persona, and ideology_score are never rendered.

The values left out as missing are reported in one message per call, naming each field and the number of personas whose text omits it. A stated answer shown under its column name, for want of a question wording, draws a warning.

Examples

set.seed(110)
panel <- panel_from_personas(anes_2024_personas, n = 8)
panel

# A labeled column, as haven::read_dta() returns it, is decoded from its
# labels: the unlabeled 5 is placed on its scale and -9 is left out.
survey <- data.frame(age = c("18-29", "65 or older", "30-49"))
survey$services <- structure(c(1, 5, -9), labels = c(
  "1. Government should provide many fewer services" = 1,
  "7. Government should provide many more services" = 7,
  "-9. Refused" = -9))
labeled_panel <- panel_from_personas(
  survey, questions = c(services = "Government services and spending"))
labeled_panel$persona