AI Notes Article Brainstorming

Load/Prep Data

Here is how I’m classifying the tools, please let me know if there are any corrections required:

  • Generic

    • Fathom
    • FieldKo
    • Fieldnote.ai
    • Fireflies
    • Otter
    • Sembly
    • Supernormal
    • tldv
    • Zoom
  • Industry

    • Finmate AI
    • Jump
    • Vega
    • Zeplyn
    • Zocks
  • Unable to classify

    • Other
    • Unsure
Show the code
library(tidyverse)
library(scales)
setwd('~/Downloads')
prod <- read.csv('profiles.csv')

prod <- prod %>% 
  mutate(swai3 = case_when(swai == 'No' ~ 'No',
                           (swaifathom == 1 | swaifieldko == 1 | swaifilenote == 1 | 
                           swaifireflies == 1 | swaiotter == 1 | swaisembly == 1 |
                             swaisupernormal == 1 | swaitldv == 1 | swaizoom == 1) & 
                             (is.na(swaifinmate) & is.na(swaijump) & is.na(swaivega) &
                             is.na(swaizeplyn) & is.na(swaizocks)) ~ 'Generic',
                           
                           swaifinmate == 1 | swaijump == 1 | swaivega == 1 |
                             swaizeplyn == 1 | swaizocks == 1 ~ 'Industry',
                           
                           swaioth == 1 | swaiunsure == 1 ~ 'Unable to Classify'),
        swai3 = factor(swai3, 
                       levels = c('No', 'Generic', 'Industry', 'Unable to Classify')) ,
        shareprepservice = hrshrclserv + hrshrprep,
        totalprepservice = hrsclserv + hrsprep)

Some descriptive stats to help you set the stage

Show the code
prod %>% 
  drop_na(swai3) %>%
  count(swai3) %>% 
  mutate(pct = prop.table(n),
         label = scales::percent(pct, accuracy = 0.1)) %>%  
  ggplot(aes(x = "", y = pct, fill = swai3)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  theme_void() +
  labs(fill = "swai3", title = "Use of AI Meeting Notes Tools") +
  theme(legend.position = "right") +
  geom_text(aes(label = label), 
            position = position_stack(vjust = 0.5), 
            size = 3)  

At this point, you may want to follow up with Figure 4.45 and Figure 4.46 in the study and note that while generic tools are more widely adopted, the most highly rated tool is an industry solution (Jump)

Breaking down the workweek by use of AI notes (50+ clients/advisor)

Show the code
prod %>% 
  filter(swai3 != '',
         swai3 != 'Unable to Classify',
         clxadv >= 50) %>% 
  drop_na(swai3) %>%
  group_by(swai3) %>% 
  summarize(`Meeting Preparation` = mean(hrshrprep, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Meeting Preparation`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = scales::percent(`Meeting Preparation`, accuracy = 0.1)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Share of Workweek Preparing for Client Meetings',
       fill = 'Use AI Notes') +
  scale_y_continuous(labels = scales::percent,
                     limits = c(0, .15)) +
  theme_bw()

Show the code
prod %>% 
  filter(swai3 != '',
         swai3 != 'Unable to Classify',
         clxadv >= 50) %>% 
  drop_na(swai3) %>%
  group_by(swai3) %>% 
  summarize(`Meeting Preparation` = mean(hrsprep, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Meeting Preparation`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = round(`Meeting Preparation`, digits = 2)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Weekly Hours Preparing for Client Meetings',
       fill = 'Use AI Notes') +
  theme_bw()

Show the code
prod %>% 
  filter(swai3 != '',
         swai3 != 'Unable to Classify',
         clxadv >= 50) %>% 
  drop_na(swai3) %>%
  group_by(swai3) %>% 
  summarize(`Client Service` = mean(hrshrclserv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Client Service`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = scales::percent(`Client Service`, accuracy = 0.1)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Share of Workweek Doing Client Servicing',
       fill = 'Use AI Notes') +
  scale_y_continuous(labels = scales::percent,
                     limits = c(0, .15)) +
  theme_bw()

Show the code
prod %>% 
  filter(swai3 != '',
         swai3 != 'Unable to Classify',
         clxadv >= 50) %>% 
  drop_na(swai3) %>%
  group_by(swai3) %>% 
  summarize(`Client Service` = mean(hrsclserv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Client Service`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = round(`Client Service`, digits = 2)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Weekly Hours Doing Client Servicing',
       fill = 'Use AI Notes') +
  theme_bw()

Share of Workweek Doing Meeting Prep by Use of AI Notes and Practice Structure (50+ clients/advisor)

Show the code
prod %>% 
  filter(swai3 != '',
         clxadv >= 50,
         swai3 != 'Unable to Classify',
         pstructure != ''
        ) %>% 
  drop_na(swai3, pstructure, pcycle) %>%
  group_by(pstructure, swai3) %>% 
  summarize(`Meeting Preparation` = mean(hrshrprep, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Meeting Preparation`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = scales::percent(`Meeting Preparation`, accuracy = 0.1)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Share of Workweek Preparing for Client Meetings',
       fill = 'Use AI Notes') +
  scale_y_continuous(labels = scales::percent,
                     limits = c(0, 0.2)) +
  theme_minimal() +
  facet_wrap(~pstructure)

Show the code
prod %>% 
  filter(swai3 != '',
         clxadv >= 50,
         swai3 != 'Unable to Classify',
         pstructure != ''
        ) %>% 
  drop_na(swai3, pstructure, pcycle) %>%
  group_by(pstructure, swai3) %>% 
  summarize(`Meeting Preparation` = mean(hrsprep, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Meeting Preparation`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = round(`Meeting Preparation`, digits = 2)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Weekly Hours Preparing for Client Meetings',
       fill = 'Use AI Notes') +
  theme_minimal() +
  facet_wrap(~pstructure) +
  ylim(0,8)

Show the code
prod %>% 
  filter(swai3 != '',
         clxadv >= 50,
         swai3 != 'Unable to Classify',
         pstructure != ''
        ) %>% 
  drop_na(swai3, pstructure, pcycle) %>%
  group_by(pstructure, swai3) %>% 
  summarize(`Client Service` = mean(hrshrclserv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Client Service`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = scales::percent(`Client Service`, accuracy = 0.1)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Share of Workweek Servicing Clients',
       fill = 'Use AI Notes') +
  scale_y_continuous(labels = scales::percent,
                     limits = c(0, 0.25)) +
  theme_minimal() +
  facet_wrap(~pstructure)

Show the code
prod %>% 
  filter(swai3 != '',
         clxadv >= 50,
         swai3 != 'Unable to Classify',
         pstructure != ''
        ) %>% 
  drop_na(swai3, pstructure, pcycle) %>%
  group_by(pstructure, swai3) %>% 
  summarize(`Client Service` = mean(hrsclserv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Client Service`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = round(`Client Service`, digits = 2)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Weekly Hours Servicing Clients',
       fill = 'Use AI Notes') +
  theme_minimal() +
  facet_wrap(~pstructure) +
  ylim(0,9)

Revenue Per Advisor By Use of AI Meeting Notes (50+ Clients/Advisor)

Show the code
prod %>% 
  filter(clxadv >= 50,
         swai3 != 'Unable to Classify') %>%
  mutate(pstructure3 = ifelse(pstructure %in% c('Silo', 'Ensemble'),
                              'Silo/Ensemble', pstructure)) %>%
  drop_na(swai3) %>%
  group_by(pstructure3, swai3) %>% 
  summarize(`Revenue Per Advisor` = median(rvxadv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')), 
             y = `Revenue Per Advisor`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = dollar(`Revenue Per Advisor`)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Median Revenue Per Advisor',
       fill = 'Use AI Notes') +
  theme_minimal() +
  facet_wrap(~pstructure3, ncol = 1) +
  ylim(0, 1000000)

Show the code
prod %>% 
  filter(clxadv >= 50,
         swai != '') %>%
  mutate(pstructure3 = ifelse(pstructure %in% c('Silo', 'Ensemble'),
                              'Silo/Ensemble', pstructure)) %>%
  drop_na(swai) %>%
  group_by(pstructure3, swai) %>% 
  summarize(`Revenue Per Advisor` = median(rvxadv, na.rm = TRUE)) %>% 
  mutate(useai = ifelse(swai == 'No', 'No', 'Yes')) %>% 
  ggplot(aes(x = factor(swai), 
             y = `Revenue Per Advisor`,
             fill = useai)) +
  geom_col(color = 'black') +
  geom_text(aes(label = dollar(`Revenue Per Advisor`)), 
            vjust = -0.5, size = 5) +  
  labs(x = 'Use of AI Notes', 
       y = 'Median Revenue Per Advisor',
       fill = 'Use AI Notes') +
  theme_minimal() +
  facet_wrap(~pstructure3, ncol = 1)+
  ylim(0, 1000000)

2022-2024 Comparison

Here I’ve merged the 2022 and 2024 data like you’ve asked. Unfortunately, (1) the samples are small, ~100, so I can’t breakdown the AI notes people into industry/generic, and (2) the results aren’t meaningful at all.

Show the code
prod22 <- read.csv('~/Downloads/prod22.csv')
prod$year <- '2024'
prod22$year <- '2022'

# prod24x <- prod %>% select(swai, hrshrprep, hrshrclserv, hrsprep, hrsclserv, year, rvxadv, pcycle, pstructure, clxadv) %>% mutate()
# prod22x <- prod22 %>% select(hrshrprep, hrshrclserv, hrsprep, hrsclserv, year, revxadv, pcycle, pstructure, clxadv) %>% mutate(rvxadv = revxadv)
# 
# prodboth <- bind_rows(prod24x, prod22x)

prod$clientserv24 <- prod$hrsclserv
prod$meetprep24 <- prod$hrsprep
prod$prepandservice24 <- prod$hrsprep + prod$hrsclserv

prod$rvxadv24 <- prod$rvxadv

prod22$clientserv22 <- prod22$hrsclserv
prod22$meetprep22 <- prod22$hrsprep
prod22$rvxadv22 <- prod22$revxadv
prod22$prepandservice22 <- prod22$hrsprep + prod22$hrsclserv

prodboth <- merge(prod22, prod, by = 'email'); dim(prodboth)
[1]  105 2511
Show the code
prodboth %>% 
  filter(swai != '',
         clxadv.y >= 50) %>%
  group_by(swai) %>% 
  summarize(clientserv22 = mean(clientserv22, na.rm = T),
            clientserv24 = mean(clientserv24, na.rm = T))
# A tibble: 2 × 3
  swai  clientserv22 clientserv24
  <chr>        <dbl>        <dbl>
1 No            5.74         6.01
2 Yes           3.33         6.12
Show the code
prodboth %>% 
  filter(swai != '',
         clxadv.y >= 50) %>%
  group_by(swai) %>% 
  summarize(meeprep22 = mean(meetprep22, na.rm = T),
            meeprep24 = mean(meetprep24, na.rm = T))
# A tibble: 2 × 3
  swai  meeprep22 meeprep24
  <chr>     <dbl>     <dbl>
1 No         4.81      4.25
2 Yes        5.04      4.76
Show the code
prodboth %>% 
  filter(swai != '',
         clxadv.y >= 50) %>%
  group_by(swai) %>% 
  summarize(prepandservice22 = mean(prepandservice22, na.rm = T),
            prepandservice24 = mean(prepandservice24, na.rm = T))
# A tibble: 2 × 3
  swai  prepandservice22 prepandservice24
  <chr>            <dbl>            <dbl>
1 No               10.6              10.3
2 Yes               8.38             10.9
Show the code
prodboth %>% 
  filter(swai != '',
         clxadv.y >= 50) %>%
  group_by(swai) %>% 
  summarize(rvxadv22 = median(rvxadv22, na.rm = T),
            rvxadv24 = median(rvxadv24, na.rm = T))
# A tibble: 2 × 3
  swai  rvxadv22 rvxadv24
  <chr>    <dbl>    <dbl>
1 No      512500  666667.
2 Yes     385000  480000 

Saving old code for later

Show the code
# prod %>%
#   filter(swai3 != '',
#          swai3 != 'Unable to Classify') %>%
#   mutate(clxadv2 = ifelse(clxadv >= 50,
#                           '50+ Clients Per Advisor',
#                           '<50 Clients Per Advisor')) %>%
#   drop_na(swai3, clxadv2) %>%
#   group_by(clxadv2, swai3) %>%
#   summarize(`Meeting Preparation` = mean(hrshrprep, na.rm = TRUE)) %>%
#   mutate(useai = ifelse(swai3 == 'No', 'No', 'Yes')) %>%
#   ggplot(aes(x = factor(swai3, levels = c('No', 'Industry', 'Generic')),
#              y = `Meeting Preparation`,
#              fill = useai)) +
#   geom_col(color = 'black') +
#   geom_text(aes(label = scales::percent(`Meeting Preparation`, accuracy = 0.1)),
#             vjust = -0.5, size = 5) +
#   facet_wrap(~clxadv2) +
#   labs(x = 'Use of AI Notes',
#        y = 'Share of Workweek Preparing for Client Meetings',
#        fill = 'Use AI Notes') +
#   scale_y_continuous(labels = scales::percent,
#                      limits = c(0, .15)) +
#   theme_bw()
# 
# 
# 
# 
# prod %>% 
#   filter(swai != '',
#          clxadv >= 50) %>% 
#   drop_na(swai, pstructure) %>%
#   group_by(pstructure, swai) %>% 
#   summarize(`Meeting Preparation` = mean(hrshrprep, na.rm = TRUE)) %>% 
#   ggplot(aes(x = factor(swai), 
#              y = `Meeting Preparation`,
#              fill = swai)) +
#   geom_col(color = 'black') +
#   geom_text(aes(label = scales::percent(`Meeting Preparation`, accuracy = 0.1)), 
#             vjust = -0.5, size = 5) +  
#   facet_wrap(~clxadv2) +
#   labs(x = 'Use of AI Notes', 
#        y = 'Share of Workweek Preparing for Client Meetings',
#        fill = 'Use AI Notes') +
#   scale_y_continuous(labels = scales::percent,
#                      limits = c(0, 0.2)) +
#   theme_minimal() +
#   facet_wrap(~pstructure)