This project explores self-reported symptom triggers using survey data and R. The analysis cleans and restructures the original dataset, searches free-text responses for common trigger categories, counts how frequently those triggers are associated with different symptoms, and visualizes the results.
The dataset is not included in this repository.
To request the CSV file (symptomtriggers.csv), please email:
Dylan Armbruster dylanarmbruster15@gmail.com
Once obtained, place symptomtriggers.csv in the working directory containing the R script.
The dataset contains participant responses describing factors that trigger or worsen a variety of symptoms.
The analysis follows several main steps:
- Load and inspect the raw survey data.
- Remove unnecessary identifier columns.
- Rename survey variables to more readable symptom names.
- Reshape the dataset from wide to long format.
- Search free-text responses for selected trigger-related keywords.
- Count trigger mentions for each symptom.
- Create visualizations showing which symptoms are associated with each trigger category.
This analysis was written in R and uses the following packages:
library(tidyverse)
library(readr)
library(reshape2)
library(tibble)Install any missing packages before running the analysis:
install.packages(c(
"tidyverse",
"readr",
"reshape2",
"tibble"
))The CSV file is imported with read_csv() and converted to a tibble:
RawData <- read_csv("symptomtriggers.csv")
df <- as_tibble(RawData)The first several rows can be inspected with:
head(df)Missing values are checked using:
sum(is.na(df))The original psid column is removed:
df <- df %>%
mutate(psid = NULL)The remaining survey variables are then renamed to make the symptom names easier to interpret.
Examples include:
Renamed_df <- df %>%
rename(
Subject = psid2,
Fatigue = opensxtrig_fatigue,
LightHeaded = opensxtrig_lhead,
Vertigo = opensxtrig_vert,
Cognitive = opensxtrig_cog,
Tinnitus = opensxtrig_tin,
Nausea = opensxtrig_naus,
Weakness = opensxtrig_weak
)The full script contains the complete mapping between the original survey variable names and their cleaned symptom names.
An early part of the analysis examines the frequency of individual responses for selected symptoms.
For example:
FatigueMC <- Renamed_df %>%
count(Fatigue, sort = TRUE)
LightHeadedMC <- Renamed_df %>%
count(LightHeaded, sort = TRUE)
VertigoMC <- Renamed_df %>%
count(Vertigo, sort = TRUE)This approach was useful for initial exploration, although free-text responses make exact word-frequency analysis difficult because respondents may describe similar triggers using different words or phrases.
The symptom columns are transformed from wide format to long format using pivot_longer():
df_LongForm <- Renamed_df %>%
pivot_longer(
cols = Fatigue:BowelBladderProblems,
values_to = "Responses"
) %>%
rename(Symptom = name)The resulting dataset is reduced to three primary columns:
df_LongForm <- df_LongForm %>%
select(Subject, Symptom, Responses)The resulting structure is approximately:
| Subject | Symptom | Responses |
|---|---|---|
| Participant ID | Fatigue | Free-text trigger response |
| Participant ID | Vertigo | Free-text trigger response |
| Participant ID | Pain | Free-text trigger response |
This format makes it easier to analyze trigger responses across many symptoms.
Because the survey responses are free text, trigger categories were created using keyword matching.
The current analysis includes the following trigger categories:
- Fatigue
- Menstrual cycle / periods
- Relapse
- Physical activity
- Stress
- Temperature
- Food
- Sleep
- Infection
Keywords were chosen manually after reviewing the dataset and considering words that represented similar concepts.
For example, the Physical Activity category includes terms related to exercise, exertion, chores, climbing, movement, walking, and standing.
Trigger frequencies are calculated using grepl() with case-insensitive regular expressions.
For example:
CountsTable <- df_LongForm %>%
group_by(Symptom) %>%
summarize(
FatigueCount = sum(
grepl(
"(Fatigue)|(Fatigued)|(Tiredness)",
Responses,
ignore.case = TRUE
)
),
StressCount = sum(
grepl(
"(Stress)",
Responses,
ignore.case = TRUE
)
),
TemperatureCount = sum(
grepl(
"(Heat)|(Hot)|(Warm)|(Cold)",
Responses,
ignore.case = TRUE
)
),
FoodCount = sum(
grepl(
"(Food)|(Histamine)|(Eating)|(Diet)",
Responses,
ignore.case = TRUE
)
),
SleepCount = sum(
grepl(
"(Sleep)",
Responses,
ignore.case = TRUE
)
)
) %>%
ungroup()Each row of CountsTable represents a symptom, while each count column represents the number of responses containing keywords associated with a particular trigger.
The project uses ggplot2 to create bar charts showing how frequently each trigger is mentioned for each symptom.
A basic fatigue-trigger plot, for example, is generated with:
ggplot(
CountsTable,
aes(x = Symptom, y = FatigueCount)
) +
geom_bar(stat = "identity", fill = "blue") +
labs(
title = "Symptoms Triggered by Fatigue",
x = "Symptom",
y = "Fatigue Frequency"
) +
theme(
axis.text.x = element_text(
size = 9,
angle = 70,
hjust = 1
)
)Similar plots are created for:
- Periods
- Relapse
- Physical activity
- Stress
- Temperature
- Food
- Sleep
- Infection
The script also creates filtered plots that exclude symptoms with a trigger count of zero, making the strongest relationships easier to see.
The counts in this analysis represent keyword matches in self-reported free-text responses. They should not automatically be interpreted as clinical associations or causal relationships.
For example, if StressCount is high for a particular symptom, this means that words associated with stress appeared relatively frequently in responses for that symptom. It does not establish that stress causes the symptom.
The analysis is intended primarily as an exploratory way to summarize patterns in the survey responses.
This analysis uses manually selected regular-expression keywords to classify free-text responses. As a result, several limitations should be considered:
- Respondents may use synonyms that are not included in the keyword lists.
- A keyword may occasionally appear in a context that does not indicate a trigger.
- Spelling variations and misspellings may cause responses to be missed.
- Some categories contain broader keyword sets than others.
- Keyword selection involves subjective judgment.
- A single response may contain multiple trigger categories.
- The analysis measures mentions, not necessarily unique participants.
- Free-text responses may require additional preprocessing for more rigorous natural-language analysis.
Because of these limitations, the results are best treated as exploratory rather than definitive.
Future versions of the analysis could improve the text-processing methodology by:
- Standardizing spelling and punctuation.
- Tokenizing responses into individual words or phrases.
- Applying stemming or lemmatization.
- Creating a documented trigger dictionary.
- Counting unique participants rather than only keyword occurrences.
- Separating phrases such as
"not stress"from positive trigger mentions. - Using more sophisticated natural-language processing methods.
- Combining related trigger terms into reproducible categories.
- Calculating proportions in addition to raw counts.
- Creating heatmaps or other visual summaries of symptom-trigger relationships.
- Install R and the required packages.
- Request
symptomtriggers.csvusing the email address above. - Place the CSV file in the same working directory as the analysis script.
- Open the R script or R project.
- Run the script from top to bottom.
- Review
CountsTableand the generated plots.
A simple project structure may look like:
symptom-trigger-analysis/
├── README.md
├── symptom_trigger_analysis.R
└── symptomtriggers.csv # Not included in repository
This project analyzes self-reported survey data for exploratory and research purposes. The results should not be interpreted as medical advice, diagnosis, or evidence that a particular trigger causes a particular symptom.
Questions or requests for access to the dataset can be sent to:
Dylan Armbruster dylanarmbruster15@gmail.com