This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.
When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
#install.packages("jsonlite")
#install.packages("lubridate")
#install.packages("dyplr")
#install.packages("readxl")
#install.packages("revgeo")
#install.packages("psych")
library(psych)
## Warning: package 'psych' was built under R version 4.4.1
library(revgeo)
## Warning: package 'revgeo' was built under R version 4.4.1
library(readxl)
## Warning: package 'readxl' was built under R version 4.4.1
library(lubridate)
## Warning: package 'lubridate' was built under R version 4.4.1
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(jsonlite)
## Warning: package 'jsonlite' was built under R version 4.4.1
# Audio started: When a user begins playing a meditation.
# Audio completed: When a user finishes listening to a meditation.
# Track completed: Triggered either when a user completes a meditation or swipes on a track in a pack. They can also remove a track from the list by swiping again (this # action doesn’t impact the “audio completed” status).
# Track rated: Optional rating after a meditation. Users can only rate after listening to a meditation, but they are not required to rate it.
# User stats: Automatically calculated totals per user, which they can also manually edit.
# data = tracks completed
# na_data = users who have not started any meditations
# app_loaded = app opens
# user_stats
opens <- fromJSON("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/app-loaded.json")
stats <- fromJSON("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/output/users-stats.json")
write.csv(stats, "users-stats.csv")
tracks <- fromJSON("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/output/audio-completed.json")
write.csv(tracks, "audio-completed.csv")
#opens <- fromJSON("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/tracks-completed-fresh-11092024.json")
na_opens <- readLines("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/output/na-app-loaded.txt")
## Warning in readLines("C:/Users/juadams/Documents/Needed Docs/Research
## (Apps)/Medito_data/output/na-app-loaded.txt"): incomplete final line found on
## 'C:/Users/juadams/Documents/Needed Docs/Research
## (Apps)/Medito_data/output/na-app-loaded.txt'
#write.csv(na_opens, "na-app-loaded.csv")
tracks_started <- fromJSON("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/output/audio-started.json")
# NOTE: Remove those with no opens?
# NOTE: Change NAs to 0? Currently when no tracks completed, the user will get NA for file_duration. Currently this IS being done at like 249.
# Load the engagement user data
#data <- read.csv("C:/Users/juadams/Documents/Research (Apps)/Medito_output_20072024/output/tracks_completed_14082024.csv")
#opens <- read.csv("C:/Users/juadams/Documents/Research (Apps)/Medito_data/output/app-loaded.csv")
#stats <- read.csv("C:/Users/juadams/Documents/Research (Apps)/Medito_data/output/app-loaded.csv")
# Load the qualtrics user data
results <- read.csv("C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/Medito Engagement - Baseline_September 16, 2024_00.47.csv")
# Convert to data frame for analysis
tracks <- as.data.frame(tracks)
# Save the data frame to a CSV file
#write.csv(df, file = "C:/Users/juadams/Documents/Research #(Apps)/Medito_output_20072024/output/tracks_completed_14082024.csv", row.names = FALSE)
# Load the CSV file with the ID replacements
file_path <- "C:/Users/juadams/Documents/Needed Docs/Research (Apps)/Medito_data/invalid_ids_06092024.csv"
id_replacements <- read.csv(file_path)
#NOTE: Should the invalid ID replacement be done AFTER the merge? I think at this stage means that the data gets merged better i.e. right ID from medito matched.
# Replace missing Medito_ID values with Q_Medito_ID values
results <- results %>%
mutate(Medito_ID = ifelse(is.na(Medito_ID), Q_Medito_ID, Medito_ID))
# Remove rows with empty Medito_ID
#results <- results %>%
# filter(Medito_ID != "")
# Check for duplicate Medito_IDs
duplicate_ids <- results %>%
filter(!is.na(Medito_ID) & Medito_ID != "") %>% # Exclude NAs and empty strings
group_by(Medito_ID) %>%
filter(n() > 1) %>%
ungroup()
library(dplyr)
library(dplyr)
library(dplyr)
# Step 1: Count non-NA values for each row and add it as a new column
results <- results %>%
mutate(non_na_count = rowSums(!is.na(across(everything()))))
# Step 2: For each Medito_ID, keep the row with the maximum non-NA count
results_cleaned <- results %>%
group_by(Medito_ID) %>%
filter(non_na_count == max(non_na_count)) %>%
ungroup() %>%
# Step 3: Remove any exact duplicates (where all values are the same)
distinct()
# Display the number of rows removed
cat("Duplicates handled and cleaned. Number of rows removed:", nrow(results) - nrow(results_cleaned), "\n")
## Duplicates handled and cleaned. Number of rows removed: 0
# Initialize vectors to store valid and invalid IDs
valid_ids <- c()
invalid_ids <- c()
#REad the CSV file containing columns A (old IDs) and B (new IDs)
id_replacements <- read.csv(file_path)
# Use a left join to replace any matching IDs in results$Medito_ID with those in column B
results <- results %>%
left_join(id_replacements, by = c("Medito_ID" = "A")) %>%
mutate(ID_replaced = ifelse(!is.na(B) & Medito_ID != B, TRUE, FALSE),
Medito_ID = ifelse(!is.na(B), B, Medito_ID))
## Warning in left_join(., id_replacements, by = c(Medito_ID = "A")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 753 of `x` matches multiple rows in `y`.
## ℹ Row 25 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
# select(-B) # Remove the column B after replacement
# Print the IDs that were replaced and their new values
replaced_ids <- results %>%
filter(ID_replaced == TRUE) %>%
select(Old_ID = Medito_ID, New_ID = B)
# Print the list of replaced IDs
print(replaced_ids)
## Old_ID New_ID
## 1 163d2956-17d2-4d6d-a337-03c7a22a394a 163d2956-17d2-4d6d-a337-03c7a22a394a
## 2 6abe7576-5d47-4f17-81e3-644728584cbb 6abe7576-5d47-4f17-81e3-644728584cbb
## 3 39bb6f30-31bd-466b-9ab3-1285815aeba8 39bb6f30-31bd-466b-9ab3-1285815aeba8
## 4 6b935c96-bd4b-4095-8e85-93efa73dd0bb 6b935c96-bd4b-4095-8e85-93efa73dd0bb
## 5 client_clyxqq4n7000000i8v0452o4z client_clyxqq4n7000000i8v0452o4z
## 6 1cc30a0f-fa06-4f59-a83e-41cc59763a0c 1cc30a0f-fa06-4f59-a83e-41cc59763a0c
## 7 a120bfde-49d6-4221-b18a-99900064c398 a120bfde-49d6-4221-b18a-99900064c398
## 8 client_clyz6c3d1000000i8dw2ubivf client_clyz6c3d1000000i8dw2ubivf
## 9 client_clyzi8qfo000000i87sqmlwe4 client_clyzi8qfo000000i87sqmlwe4
## 10 client_clz250871000000i8hbl26ygx client_clz250871000000i8hbl26ygx
## 11 942c07c8-2a6d-48a8-8950-748afd47e0cb 942c07c8-2a6d-48a8-8950-748afd47e0cb
## 12 4097f545-a285-4ad9-b73e-583aadf90698 4097f545-a285-4ad9-b73e-583aadf90698
## 13 client_cm01ex817000000i8qsc30h2s client_cm01ex817000000i8qsc30h2s
## 14 client_cm018a0gs000000i8pqogkeri client_cm018a0gs000000i8pqogkeri
## 15 client_cm01tpuj7000000i8zel2w8kk client_cm01tpuj7000000i8zel2w8kk
## 16 client_cm0297w1p000000i82hkprfhj client_cm0297w1p000000i82hkprfhj
## 17 client_cm021g621000000i86kjor68f client_cm021g621000000i86kjor68f
## 18 client_cm021g621000000i86kjor68f client_cm021g621000000i86kjor68f
## 19 client_cm029sq5z000000i8lmixi80k client_cm029sq5z000000i8lmixi80k
## 20 client_cm02phv4p000000i8cldte9jb client_cm02phv4p000000i8cldte9jb
## 21 client_37zzc000000i8v3mi3ce3 client_37zzc000000i8v3mi3ce3
## 22 client_cm02tguuk000000i8if0ky82v client_cm02tguuk000000i8if0ky82v
## 23 client_cm02h9p50000000i8qjcxbf6r client_cm02h9p50000000i8qjcxbf6r
## 24 Graham58 Graham58
## 25 client_cm032027z000000i8rtedpyqh client_cm032027z000000i8rtedpyqh
## 26 client_cm021g621000000i86kjor68f client_cm021g621000000i86kjor68f
## 27 client_cm021g621000000i86kjor68f client_cm021g621000000i86kjor68f
## 28 client_cm034lyji000000i834w0tr1f client_cm034lyji000000i834w0tr1f
## 29 client_cm03jb45w000000i8jyemrgex client_cm03jb45w000000i8jyemrgex
## 30 client_cm03u372n000000i87x8ndwtt client_cm03u372n000000i87x8ndwtt
## 31 client_cm03w1p03000000i87jlv9l0u client_cm03w1p03000000i87jlv9l0u
## 32 client_cm03sxyyh000000i8mo0646d4 client_cm03sxyyh000000i8mo0646d4
## 33 client_cm04a9nvv000000i8urb7c75f client_cm04a9nvv000000i8urb7c75f
## 34 client_cm04lxjnb000000i8isc1mart client_cm04lxjnb000000i8isc1mart
## 35 client_05d971r000000i8b9dn30ow client_05d971r000000i8b9dn30ow
## 36 client_cm04rtyvs000000i8wlihcu7v client_cm04rtyvs000000i8wlihcu7v
## 37 client_cm05jimro000000i8w0v8a5ql client_cm05jimro000000i8w0v8a5ql
## 38 client_cm06hakyd000000i85z4ivumx client_cm06hakyd000000i85z4ivumx
## 39 client_cm06igj74000000i8eukt751p client_cm06igj74000000i8eukt751p
## 40 client_06v04nj000000i83a0x9vdv client_06v04nj000000i83a0x9vdv
## 41 client_cm076abrh000000i8zva9nnwj client_cm076abrh000000i8zva9nnwj
## 42 client_cm07g3rg3000000i8h4ck6ivc client_cm07g3rg3000000i8h4ck6ivc
## 43 client_cm07lou2x000000i8ul0s6kat client_cm07lou2x000000i8ul0s6kat
## 44 client_cm05okxu6000000i8rmdtbqew client_cm05okxu6000000i8rmdtbqew
## 45 client_cm07q2tss000000i8qf85awsd client_cm07q2tss000000i8qf85awsd
## 46 client_cm0827pil000000i8vd5szh57 client_cm0827pil000000i8vd5szh57
## 47 client_cm0881cm6000000i8p2jjf92j client_cm0881cm6000000i8p2jjf92j
## 48 client_cm088hxi6000000i8vvfoo54e client_cm088hxi6000000i8vvfoo54e
## 49 client_client_cm081ir0m000000i8u5ilx client_client_cm081ir0m000000i8u5ilx
# Print the quantity of replacements
cat("Number of IDs replaced: ", nrow(replaced_ids), "\n")
## Number of IDs replaced: 49
# Clean up the results dataframe by removing temporary columns
results <- results %>%
select(-B, -ID_replaced)
# Save the invalid IDs to a CSV file
#write.csv(invalid_ids, "C:/Users/juadams/Documents/Research (Apps)/Medito_data/invalid_ids_new_06092024.csv", row.names = FALSE)
# Cutoff non-new users
# Define the cutoff date
cutoff_date <- 1710844800
cutoff_date <- cutoff_date * 1000
# Identify users who have opens before the cutoff date
users_before_cutoff <- opens %>%
filter(created < cutoff_date) %>%
distinct(user_id)
# Number of users with events before the cutoff
num_users_before_cutoff <- nrow(users_before_cutoff)
cat("Number of users with events before the cutoff date:", num_users_before_cutoff, "\n")
## Number of users with events before the cutoff date: 0
# Print the earliest 'created' timepoint and number of users with events before cutoff
earliest_created_time <- min(opens$created, na.rm = TRUE)
cat("Earliest 'created' timepoint in the dataset:", earliest_created_time, "\n")
## Earliest 'created' timepoint in the dataset: 1.721251e+12
# Identify users who have TRACKs before the cutoff date
users_before_cutoff_tracks <- tracks %>%
filter(created < cutoff_date) %>%
distinct(user_id)
# Number of users with events before the cutoff
num_users_before_cutoff <- nrow(users_before_cutoff_tracks)
cat("Number of users with tracks before the cutoff date:", num_users_before_cutoff, "\n")
## Number of users with tracks before the cutoff date: 175
# Print the earliest 'created' timepoint and number of users with events before cutoff
earliest_created_time <- min(tracks$created, na.rm = TRUE)
cat("Earliest 'created' timepoint in the tracks dataset:", earliest_created_time, "\n")
## Earliest 'created' timepoint in the tracks dataset: 1.697432e+12
# For opens
if (!inherits(opens$created, "POSIXct")) {
opens <- opens %>%
mutate(created_date = as.POSIXct(created / 1000, origin = "1970-01-01", tz = "UTC"))
}
# For tracks
if (!inherits(tracks$created, "POSIXct")) {
tracks <- tracks %>%
mutate(created_date = as.POSIXct(created / 1000, origin = "1970-01-01", tz = "UTC"))
}
# For tracks
if (!inherits(tracks_started$created, "POSIXct")) {
tracks <- tracks %>%
mutate(created_date = as.POSIXct(created / 1000, origin = "1970-01-01", tz = "UTC"))
}
# Adjust device times to the right format
#opens <- opens %>%
# mutate(device_time = as.POSIXct(device_time, format = "%Y-%m-%d %H:%M:%S"))
# Get the earliest device time for each user
earliest_opens <- opens %>%
group_by(user_id) %>%
summarize(earliest_device_time = min(created), .groups = 'drop')
# Join earliest_opens with tracks to include all users, regardless of whether they have tracks
tracks_with_earliest <- earliest_opens %>%
left_join(tracks, by = "user_id")
filtered_tracks_30 <- tracks_with_earliest %>%
filter(is.na(created) | (created >= earliest_device_time & created <= (earliest_device_time + 30*24*60*60)))
filtered_tracks <- filtered_tracks_30 %>%
filter(!user_id %in% users_before_cutoff$user_id)
#NOTE: This doesn't remove anything from results?
filtered_results <- results %>%
filter(!Medito_ID %in% users_before_cutoff_tracks$user_id)
results <- filtered_results
# Remaining after filtering non-new in filtered_tracks: 899 obs. 175 non-new users completed survey.
# Load necessary libraries
library(dplyr)
library(lubridate)
# Convert milliseconds to seconds and then to POSIXct
earliest_opens <- earliest_opens %>%
mutate(
earliest_device_time = as.numeric(earliest_device_time) / 1000, # Convert milliseconds to seconds
earliest_device_time = as.POSIXct(earliest_device_time, origin = "1970-01-01", tz = "UTC") # Convert to POSIXct
)
print(earliest_opens)
## # A tibble: 1,014 × 2
## user_id earliest_device_time
## <chr> <dttm>
## 1 0054cc73-ba11-462c-a6e0-782048467d54 2024-07-21 04:01:38
## 2 005d02a2-fde1-4e0c-9aa8-0249c4e39a30 2024-08-19 16:24:11
## 3 00d0aaf2-4af1-4ca9-a213-937aff38a891 2024-07-19 06:35:11
## 4 0150ef07-6c5f-4471-a6e8-93e24a74e103 2024-07-27 04:03:14
## 5 01a42e4a-8d1a-4e4c-9d6b-c35fb085a129 2024-07-19 21:19:36
## 6 03028622-04e6-48b9-ae73-33c4b0ae5b2f 2024-07-24 08:48:44
## 7 040d054f-4eac-4515-855f-471820fd91d4 2024-07-29 20:38:54
## 8 041c8035-89b4-4e0f-89fe-5f54bc446f1f 2024-07-26 08:51:58
## 9 05db88aa-9b22-40f9-afe5-589904de7efc 2024-07-22 04:17:31
## 10 07cc87e7-74c3-4173-823e-1c9f77aff706 2024-07-18 02:56:03
## # ℹ 1,004 more rows
# Merge tracks with earliest_opens to get the first open date for each user
merged_data_tracks <- tracks %>%
left_join(earliest_opens, by = "user_id")
# Define cutoff dates
cutoff_date_1 <- as.POSIXct("2024-07-16 00:00:00", tz = "UTC")
cutoff_date_2 <- as.POSIXct("2024-08-14 00:00:00", tz = "UTC")
# Create a new column to classify users based on their first app open date
merged_data_tracks$group <- ifelse(
merged_data_tracks$earliest_device_time >= cutoff_date_1 & merged_data_tracks$earliest_device_time < cutoff_date_2,
"July20_Aug13",
ifelse(
merged_data_tracks$earliest_device_time >= cutoff_date_2,
"Aug14_After",
NA_character_
)
)
# Define the cutoff date for excluding tracks
cutoff_date_exclude <- as.POSIXct("2024-07-18 00:00:00", tz = "UTC")
# Filter merged_data to exclude rows with created_date before July 18th
merged_data_filtered <- merged_data_tracks %>%
filter(created_date >= cutoff_date_exclude)
merged_data_tracks <- merged_data_filtered
# Calculate the end of the 14-day period for each user
merged_data_tracks <- merged_data_tracks %>%
mutate(end_date = earliest_device_time + days(14))
# Check the end dates
#print("Merged Data with End Dates")
#print(head(merged_data_tracks))
#print(table(merged_data_tracks$end_date))
# Filter the tracks data to include only the activities within the 14-day window
activity_data <- merged_data_tracks %>%
filter(created_date >= earliest_device_time & created_date <= end_date)
# Convert 'file_duration' to numeric if it's not already
activity_data <- activity_data %>%
mutate(file_duration = as.numeric(file_duration))
# Check the type again to confirm conversion
print("File Duration Conversion")
## [1] "File Duration Conversion"
print(str(activity_data$file_duration))
## num [1:5370] 181237 181002 181603 301531 301688 ...
## NULL
print(summary(activity_data$file_duration))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 298680 520008 574157 638520 5497887
# Sum the duration of activities within the 14-day window for each user
activity_summary <- activity_data %>%
group_by(user_id, group) %>%
summarise(total_duration = sum(file_duration, na.rm = TRUE), .groups = 'drop')
# Convert total_duration from milliseconds to minutes
activity_summary <- activity_summary %>%
mutate(total_duration_minutes = total_duration / 60000)
# Check the activity summary
print("Activity Summary")
## [1] "Activity Summary"
#print(head(activity_summary))
# Compare the two groups
summary_stats <- activity_summary %>%
group_by(group) %>%
summarise(
mean_duration = mean(total_duration_minutes, na.rm = TRUE),
sd_duration = sd(total_duration_minutes, na.rm = TRUE),
count = n(),
.groups = 'drop'
)
# Print summary statistics
print("Summary Statistics")
## [1] "Summary Statistics"
print(summary_stats)
## # A tibble: 2 × 4
## group mean_duration sd_duration count
## <chr> <dbl> <dbl> <int>
## 1 Aug14_After 56.3 71.7 139
## 2 July20_Aug13 68.6 100. 635
# Perform a t-test to compare total durations (in minutes) between the two groups
t_test_result <- t.test(total_duration_minutes ~ group, data = activity_summary)
# Print the results of the t-test
print(t_test_result)
##
## Welch Two Sample t-test
##
## data: total_duration_minutes by group
## t = -1.6998, df = 271.47, p-value = 0.09031
## alternative hypothesis: true difference in means between group Aug14_After and group July20_Aug13 is not equal to 0
## 95 percent confidence interval:
## -26.667150 1.954804
## sample estimates:
## mean in group Aug14_After mean in group July20_Aug13
## 56.25443 68.61060
# For tracks
if (!inherits(tracks_started$created, "POSIXct")) {
tracks_started <- tracks_started %>%
mutate(created_date = as.POSIXct(created / 1000, origin = "1970-01-01", tz = "UTC"))
}
# Merge tracks_started with earliest_opens to get the first open date for each user
merged_starts <- tracks_started %>%
left_join(earliest_opens, by = "user_id")
# Create a new column to classify users based on their first app open date
merged_starts$group <- ifelse(
merged_starts$earliest_device_time >= cutoff_date_1 & merged_starts$earliest_device_time < cutoff_date_2,
"July20_Aug13",
ifelse(
merged_starts$earliest_device_time >= cutoff_date_2,
"Aug14_After",
NA_character_
)
)
# Calculate the end of the 14-day period for each user
merged_starts <- merged_starts %>%
mutate(end_date = earliest_device_time + days(14))
# Filter the tracks_started data to include only the activities within the 14-day window
activity_data_starts <- merged_starts %>%
filter(created_date >= earliest_device_time & created_date <= end_date)
# Keep only the rows in activity_data_starts where user_id is present in filtered_tracks
cleaned_activity_data_starts <- activity_data_starts %>%
semi_join(filtered_tracks, by = "user_id")
activity_data_starts <- cleaned_activity_data_starts
# Convert 'file_duration' to numeric if it's not already
activity_data_starts <- activity_data_starts %>%
mutate(file_duration = as.numeric(file_duration))
# Sum the duration of activities within the 14-day window for each user in activity_data_starts
# Sum the duration of activities within the 14-day window for each user in activity_data_starts
activity_summary_starts <- activity_data_starts %>%
group_by(user_id, group) %>%
summarise(total_duration_starts = sum(file_duration, na.rm = TRUE), .groups = 'drop')
# Convert total_duration_starts from milliseconds to minutes
activity_summary_starts <- activity_summary_starts %>%
mutate(total_duration_starts_minutes = total_duration_starts / 60000)
# Merge activity_summary and activity_summary_starts to compare durations
comparison_summary <- activity_summary %>%
full_join(activity_summary_starts, by = c("user_id", "group")) %>%
mutate(
difference = total_duration_minutes - total_duration_starts_minutes
)
# Calculate summary statistics including the proportion of started to completed durations
summary_comparison <- comparison_summary %>%
group_by(group) %>%
summarise(
mean_completed_duration = mean(total_duration_minutes, na.rm = TRUE),
mean_started_duration = mean(total_duration_starts_minutes, na.rm = TRUE),
proportion_started_of_completed = (mean_completed_duration / mean_started_duration) * 100,
sd_completed_duration = sd(total_duration_minutes, na.rm = TRUE),
sd_started_duration = sd(total_duration_starts_minutes, na.rm = TRUE),
count = n(),
.groups = 'drop'
)
# Print the summary statistics for the differences and proportions
print("Summary Statistics for Differences between Completed and Started Activities")
## [1] "Summary Statistics for Differences between Completed and Started Activities"
print(summary_comparison)
## # A tibble: 2 × 7
## group mean_completed_durat…¹ mean_started_duration proportion_started_o…²
## <chr> <dbl> <dbl> <dbl>
## 1 Aug14_Aft… 56.3 71.5 78.6
## 2 July20_Au… 68.6 92.0 74.6
## # ℹ abbreviated names: ¹mean_completed_duration,
## # ²proportion_started_of_completed
## # ℹ 3 more variables: sd_completed_duration <dbl>, sd_started_duration <dbl>,
## # count <int>
# Count the number of tracks started for each user and group
tracks_started_count <- activity_data_starts %>%
group_by(user_id, group) %>%
summarise(total_tracks_started = n(), .groups = 'drop')
# Count the number of tracks completed for each user and group
tracks_completed_count <- merged_data_tracks %>%
group_by(user_id, group) %>%
summarise(total_tracks_completed = n(), .groups = 'drop')
library(tidyr)
# Merge the counts by user_id and group
comparison_counts <- tracks_started_count %>%
full_join(tracks_completed_count, by = c("user_id", "group")) %>%
replace_na(list(total_tracks_started = 0, total_tracks_completed = 0))
# Calculate the proportion of tracks started to tracks completed
comparison_counts <- comparison_counts %>%
mutate(
proportion_started_to_completed = ifelse(total_tracks_completed > 0,
total_tracks_started / total_tracks_completed,
0) # Avoid division by zero
)
# Summarize the proportions by group
summary_proportions <- comparison_counts %>%
group_by(group) %>%
summarise(
mean_proportion = mean(proportion_started_to_completed, na.rm = TRUE),
sd_proportion = sd(proportion_started_to_completed, na.rm = TRUE),
count = n(),
.groups = 'drop'
)
# Print the summary of proportions
print("Summary of Proportions")
## [1] "Summary of Proportions"
print(summary_proportions)
## # A tibble: 2 × 4
## group mean_proportion sd_proportion count
## <chr> <dbl> <dbl> <int>
## 1 Aug14_After 0.686 1.07 155
## 2 July20_Aug13 0.618 1.03 717
# Filter out NA or constant values before the t-test
valid_comparison_counts <- comparison_counts %>%
filter(!is.na(proportion_started_to_completed) & total_tracks_completed > 0)
# Perform a t-test to compare the proportions between the two groups
if (nrow(valid_comparison_counts) > 0) {
t_test_result <- t.test(proportion_started_to_completed ~ group, data = valid_comparison_counts)
# Print the results of the t-test
print(t_test_result)
} else {
print("Not enough valid data for t-test.")
}
##
## Welch Two Sample t-test
##
## data: proportion_started_to_completed by group
## t = 0.98403, df = 196.26, p-value = 0.3263
## alternative hypothesis: true difference in means between group Aug14_After and group July20_Aug13 is not equal to 0
## 95 percent confidence interval:
## -0.1001134 0.2995155
## sample estimates:
## mean in group Aug14_After mean in group July20_Aug13
## 0.7597600 0.6600589
library(dplyr)
# Assuming both dataframes have a user_id column to join on
# 1. Prepare tracks_completed_count by user
#completed_by_user <- tracks_completed_count %>%
# group_by(user_id, group) %>%
# summarise(total_completed = sum(total_tracks_completed, na.rm = TRUE), .groups = 'drop')
# 2. Prepare tracks_started_count by user
#started_by_user <- tracks_started_count %>%
# group_by(user_id, group) %>%
# summarise(total_started = sum(total_tracks_started, na.rm = TRUE), .groups = 'drop')
# 3. Merge the completed and started data by user and group
merged_data_start_comp <- tracks_completed_count %>%
inner_join(tracks_started_count, by = c("user_id", "group"))
# 4. Correlate total completed vs total started for each group
correlation_results <- merged_data_start_comp %>%
group_by(group) %>%
summarise(
correlation = cor(total_tracks_completed, total_tracks_started, method = "pearson", use = "complete.obs"),
p_value = cor.test(total_tracks_completed, total_tracks_started, method = "pearson", use = "complete.obs")$p.value,
.groups = 'drop'
)
# Print the correlation results by group
print(correlation_results)
## # A tibble: 2 × 3
## group correlation p_value
## <chr> <dbl> <dbl>
## 1 Aug14_After 0.914 1.73e-27
## 2 July20_Aug13 0.556 2.88e-37
# ERROR: Check that both time periods are filtered to 14 days?
# I.e. are the correlations between tracks started and completed in each time frame (affected = July 20 - Aug 13) and unaffected (Aug14_after) statistically different?
# This is for the count of tracks completed/started, not duration of file
# Define the correlation
r1 <- 0.9135752
r2 <- 0.5557390 # correlation from group 2
# Fisher r-to-z transformation function
fisher_r_to_z <- function(r) {
z <- 0.5 * log((1 + r) / (1 - r))
return(z)
}
# Apply the transformation to both correlations
z1 <- fisher_r_to_z(r1)
z2 <- fisher_r_to_z(r2)
# Output the z-values
z1
## [1] 1.548727
z2
## [1] 0.6266469
# Define sample sizes
n1 <- 155
n2 <- 717
# Standard error calculation
SE1 <- 1 / sqrt(n1 - 3)
SE2 <- 1 / sqrt(n2 - 3)
# Calculate the z-statistic for comparison
z_diff <- (z1 - z2) / sqrt(SE1^2 + SE2^2)
# Output the z-statistic
z_diff
## [1] 10.3224
# Calculate the p-value for a two-tailed test
p_value <- 2 * pnorm(-abs(z_diff))
p_value
## [1] 5.581145e-25
# Extract IDs from each group in activity_summary
july20_aug13_ids <- activity_summary %>%
filter(group == "July20_Aug13") %>%
select(user_id)
aug14_after_ids <- activity_summary %>%
filter(group == "Aug14_After") %>%
select(user_id)
# Join these IDs with df_stats to pull relevant stats for each group
july20_aug13_stats <- stats_filtered %>%
filter(user_id %in% july20_aug13_ids$user_id)
aug14_after_stats<- stats_filtered %>%
filter(user_id %in% aug14_after_ids$user_id)
# Join df_stats with activity_summary to bring in the group information
#df_stats_grouped <- df_stats %>%
# inner_join(activity_summary %>% select(user_id, group), by = "user_id")
# Extract IDs for each group from activity_summary
july20_aug13_ids <- activity_summary %>%
filter(group == "July20_Aug13") %>%
select(user_id)
aug14_after_ids <- activity_summary %>%
filter(group == "Aug14_After") %>%
select(user_id)
# Filter stats for each group based on the extracted IDs
july20_aug13_stats <- stats_filtered %>%
filter(user_id %in% july20_aug13_ids$user_id)
aug14_after_stats <- stats_filtered %>%
filter(user_id %in% aug14_after_ids$user_id)
# Calculate summary statistics for July20_Aug13 group
july20_aug13_summary <- july20_aug13_stats %>%
summarise(
mean_tracks_started = mean(tracks_started, na.rm = TRUE),
sd_tracks_started = sd(tracks_started, na.rm = TRUE),
mean_tracks_completed = mean(tracks_completed, na.rm = TRUE),
sd_tracks_completed = sd(tracks_completed, na.rm = TRUE),
mean_time_listened = mean(time_listened, na.rm = TRUE),
sd_time_listened = sd(time_listened, na.rm = TRUE),
mean_streak_longest = mean(streak_longest, na.rm = TRUE),
sd_streak_longest = sd(streak_longest, na.rm = TRUE)
)
# Calculate summary statistics for Aug14_After group
aug14_after_summary <- aug14_after_stats %>%
summarise(
mean_tracks_started = mean(tracks_started, na.rm = TRUE),
sd_tracks_started = sd(tracks_started, na.rm = TRUE),
mean_tracks_completed = mean(tracks_completed, na.rm = TRUE),
sd_tracks_completed = sd(tracks_completed, na.rm = TRUE),
mean_time_listened = mean(time_listened, na.rm = TRUE),
sd_time_listened = sd(time_listened, na.rm = TRUE),
mean_streak_longest = mean(streak_longest, na.rm = TRUE),
sd_streak_longest = sd(streak_longest, na.rm = TRUE)
)
# Combine summaries into a single table with separate columns for each group# Create a comparison table with formatted numbers
comparison_table <- data.frame(
Metric = c("Tracks Started", "Tracks Completed", "Time Listened", "Longest Streak"),
July20_Aug13_Mean = format(c(july20_aug13_summary$mean_tracks_started,
july20_aug13_summary$mean_tracks_completed,
july20_aug13_summary$mean_time_listened,
july20_aug13_summary$mean_streak_longest),
scientific = FALSE),
July20_Aug13_SD = format(c(july20_aug13_summary$sd_tracks_started,
july20_aug13_summary$sd_tracks_completed,
july20_aug13_summary$sd_time_listened,
july20_aug13_summary$sd_streak_longest),
scientific = FALSE),
Aug14_After_Mean = format(c(aug14_after_summary$mean_tracks_started,
aug14_after_summary$mean_tracks_completed,
aug14_after_summary$mean_time_listened,
aug14_after_summary$mean_streak_longest),
scientific = FALSE),
Aug14_After_SD = format(c(aug14_after_summary$sd_tracks_started,
aug14_after_summary$sd_tracks_completed,
aug14_after_summary$sd_time_listened,
aug14_after_summary$sd_streak_longest),
scientific = FALSE)
)
# Print the comparison table
print("Comparison Table of Summary Statistics")
print(comparison_table)
# Less for Aug 14 and after - but they've collected less data by Sep 5 date we got sent the data. I've filtered out non-new users but hard to compare after Aug 14 to before because stats are totals.