Skip to content

Extend the structured data vignette to include a best practice example for the usual tibble wrangling process with mutate #445

Description

@kleinlennart

Hi, I read through the Structured Data vignette, and I couldn't find a best practice example on how to best implement the ellmer interface for a common research analysis task in a standard dplyr rowwise operations workflow with mutate.

I would really appreciate to learn if my example below follows best practices when using ellmer (which has already been super helpful so far. Thank you for making this!).

I am also unsure how to best implement the new extract_data_parallel function since it would require making a list of prompts first and then passing them to chat, right?

📊 Rowwise analysis example with dplyr mutate

library(tidyverse)
library(ellmer)

# test survey data
df <- tibble::tribble(
  ~ID, ~question, ~assessment,
  "1", "Has your project been achieving the proposed objectives in a timely manner?", "1☐ 2☐ 3☒ 4☐ 5☐",
  "2", "Has your project been delivering the planned outputs?", "1☐ 2☐ 3☒ 4☐ 5☐",
  "3", "Have the preidentified risks been impacting the project delivery?", "1☒ 2☐ 3☐ 4☐ 5☐",
  "4", "If relevant, have your risk mitigation strategies been effectively helping manage and address risks?", "1☐ 2☐ 3☐ 4☐ 5☐"
)

# set up chat
chat <- ellmer::chat_openai(
  model = "gpt-4o",
  system_prompt = "You are a helpful assistant.",
  seed = 42,
  api_args = list(
    temperature = 0
  )
)

# define the type: expecting an integer
# required = FALSE to return NULL when missing
type_score <- type_object(
  score = ellmer::type_integer("The selected score only (values between 1 and 5). Otherwise, return 'NA'.", required = FALSE)
)

# define prompt template
user_prompt_template <- "
Here is a survey answer: '{{assessment_text}}'

Identify and extract the number that has a checked box (☒).
Return only the selected number as an integer between 1 and 5.
If none are selected or any information is missing, respond with 'NA'.
"

# define extraction function
extract_score <- function(assessment_text) {
  # every call gets its own isolated clone of chat object
  chat <- chat$clone(deep = FALSE)

  full_prompt <- ellmer::interpolate(user_prompt_template, assessment_text = assessment_text)
  result <- chat$extract_data(
    full_prompt,
    type = type_score,
    convert = TRUE,
    echo = FALSE
  )$score

  # replace NULL with NA for better readability
  result <- ifelse(is.null(result), NA, result)

  return(result)
}

# apply to all rows
df_out <- df |>
  mutate(
    score = map_int(assessment, extract_score, .progress = TRUE)
  )

df_out

Maybe a similar example could be added to the vignette since I think this is how many people would use it – happy to support if need be :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions