Spelunking macOS 'ScreenTime' App Usage & Visualizing macOS App Usage With R

Spelunking macOS 'ScreenTime' App Usage & Visualizing macOS App Usage With R

This is my learning notes for Spelunking macOS ‘ScreenTime’ App Usage with R | rud.is and Visualizing macOS App Usage with a Little Help from osqueryr & mactheknife | rud.is. These two tweets introduced some methods about how to spelunk macOS screentime app usage and visualize macOS app usage with R. Following the author’s guide, I reproduced the results of my own macOS.

The data for ‘screen time’ lurk in ~/Library/Application Support/Knowledge/knowledgeC.db this file can be processed with dplyr package. I still didn’t understand following codes completely (actually, it’s unnecessary).

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
library(tidyverse)
kdb <- src_sqlite("~/Library/Application Support/Knowledge/knowledgeC.db")
usage <- tbl(kdb, "ZOBJECT") %>%
mutate(
created_at = datetime(ZCREATIONDATE + 978307200, "UNIXEPOCH", "LOCALTIME"),
start_dow = case_when(
ZSTARTDAYOFWEEK == 1 ~ "Sunday",
ZSTARTDAYOFWEEK == 2 ~ "Monday",
ZSTARTDAYOFWEEK == 3 ~ "Tuesday",
ZSTARTDAYOFWEEK == 4 ~ "Wednesday",
ZSTARTDAYOFWEEK == 5 ~ "Thursday",
ZSTARTDAYOFWEEK == 6 ~ "Friday",
ZSTARTDAYOFWEEK == 7 ~ "Saturday"
),
start_time = datetime(ZSTARTDATE + 978307200, "UNIXEPOCH", "LOCALTIME"),
end_time = datetime(ZENDDATE + 978307200, "UNIXEPOCH", "LOCALTIME"),
usage = (ZENDDATE - ZSTARTDATE),
tz = ZSECONDSFROMGMT/3600
) %>%
left_join(tbl(kdb, "ZSTRUCTUREDMETADATA"),
c("ZSTRUCTUREDMETADATA" = "Z_PK")) %>%
left_join(tbl(kdb, "ZSOURCE"), c("ZSOURCE" = "Z_PK")) %>%
left_join(tbl(kdb, "ZSYNCPEER"), "ZDEVICEID") %>%
dplyr::filter(ZSTREAMNAME == "/app/usage") %>%
select(
app = ZVALUESTRING, created_at, start_dow, start_time, end_time, usage, tz, source = ZMODEL
) %>%
mutate(source = ifelse(is.na(source), "Other", source)) %>%
collect() %>%
mutate_at(vars(created_at, start_time, end_time), as.POSIXct)

list.files(
c("/Applications", "/System/Library/CoreServices", "/Applications/Utilities", "/System/Applications", "/Applications/Stata/", "/Applications/Stata14/", "/Applications/Stata15/", "/Applications/TeX/", "/Applications/Utilities/", "/Applications/IBM/SPSS/Statistics/26/", "/Applications/GMATPrep2012/", "/Applications/Adobe Acrobat DC/"), # main places apps are stored (there are potentially more but this is sufficient for our needs)
pattern = "\\.app$",
full.names = TRUE
) -> apps

x <- sys::exec_internal("mdls", c("-name", "kMDItemCFBundleIdentifier", "-r", apps))

x$stdout[x$stdout == as.raw(0)] <- as.raw(0x0a)

tibble(
name = gsub("\\.app$", "", basename(apps)),
app = read_lines(x$stdout)
) -> app_trans

app_trans

usage %>%
group_by(app) %>%
summarise(first = min(start_time), last = max(end_time), total = sum(usage, na.rm=TRUE)) %>%
ungroup() %>%
mutate(total = total / 60 / 60) %>% # hours
arrange(desc(total)) %>%
left_join(app_trans) -> overall_usage

overall_usage <- overall_usage %>%
mutate(
name = case_when(
grepl("com.apple.finder", app) ~ "Finder",
grepl("com.google.Chrome", app) ~ "Chrome",
grepl("com.tencent.xin", app) ~ "Wechat",
grepl("com.sublimetext.3", app) ~ "Sublime Text3",
grepl("com.netease.163music", app) ~ "Netease music",
grepl("com.apple.Terminal", app) ~ "Terminal",
grepl("org.rstudio.RStudio", app) ~ "RStudio",
grepl("com.youdao.YoudaoDict", app) ~ "Youdao Dict",
grepl("com.reederapp.macOS", app) ~ "Reeder",
grepl("com.adobe.Acrobat.Pro", app) ~ "Acrobat",
grepl("GMATPrep", app) ~ "GMAT",
grepl("com.weibo.international", app) ~ "Weibo",
grepl("com.apple.DigitalColorMeter", app) ~ "Digital Color Meter",
grepl("com.wutian.weibo", app) ~ "Weibo",
grepl("com.alipay.iphoneclient", app) ~ "Alipay",
grepl("org.julialang.launcherapp", app) ~ "Julia",
grepl("com.apple.quicktimeplayerx", app) ~ "Quick Time Player",
grepl("com.apple.archiveutility", app) ~ "Archive",
grepl("com.continuum.python", app) ~ "Python",
grepl("com.mathworks.matlab", app) ~ "MATLAB",
grepl("com.apple.Health", app) ~ "Apple Health",
grepl("com.apple.ScriptEditor2", app) ~ "Script Editor",
grepl("com.eltima.Folx3", app) ~ "Eltima",
grepl("com.apple.mobilecal", app) ~ "Apple Mobilecal",
grepl("com.apple.mobilephone", app) ~ "Mobile Phone",
grepl("com.anaconda.io", app) ~ "Anaconda",
grepl("youdaoPro", app) ~ "Youdao",
grepl("com.tencent.qqmail", app) ~ "QQ Mail",
grepl("com.aliyun.wstudio.amc.AliyunMobileApp", app) ~ "Aliyun Studio",
grepl("com.taobao.taobao4iphone", app) ~ "Taobao",
grepl("com.google.chrome.ios", app) ~ "Chrome",
grepl("reederapp", app) ~ "Reeder",
grepl("xunlei", app) ~ "Xunlei",
grepl("github", app) ~ "GitHub",
grepl("microsoft", app) ~ "Microsoft",
grepl("fournova", app) ~ "Fournova",
grepl("kugou", app) ~ "Kugou",
grepl("Preview", app) ~ "Preview",
grepl("Font", app) ~ "Font Book",
grepl("com.baidu.netdisk", app) ~ "Baidu Net Disk",
grepl("stata", app) ~ "Stata",
grepl("Kindle", app) ~ "Kindle",
grepl("Reader", app) ~ "Reader",
grepl("com.apple.TV", app) ~ "Apple TV",
grepl("mozilla", app) ~ "Firefox",
grepl("co.zeit.hyper", app) ~ "Zeit",
grepl("com.jetbrains.datagrip", app) ~ "DataGrip",
grepl("com.apple.QuickTimePlayerX", app) ~ "Quick Time Player",
grepl("pycharm", app) ~ "PyCharm",
grepl("Safari", app) ~ "Safari",
grepl("AppStore", app) ~ "AppStore",
grepl("TextEdit", app) ~ "Text Edit",
grepl("com.iqiyi.player", app) ~ "爱奇艺",
grepl("OmniGraffle", app) ~ "OmniGraffle",
grepl("com.baidu.BaiduNetdisk-mac", app) ~ "Baidu Net Disk",
grepl("com.apple.print.PrinterProxy", app) ~ "Printer",
grepl("com.apple.mail", app) ~ "Apple Mail",
grepl("com.apple.systempreferences", app) ~ "Apple System Preferences",
grepl("com.meitu.myxj", app) ~ "Meitu",
grepl("com.apple.findmy", app) ~ "Find My",
grepl("Alfred", app) ~ "Alfred",
grepl("com.jumsoft.ToolboxforPages", app) ~ "Tool Box for Pages",
grepl("com.apple.reminders", app) ~ "Apple Reminders",
grepl("com.apple.Music", app) ~ "Apple Music",
grepl("com.electron.painter", app) ~ "Painter",
grepl("net.java.openjdk.cmd", app) ~ "Java",
grepl("com.chaoui.jiongji100CN", app) ~ " Jiongji",
grepl("com.apple.Dictionary", app) ~ "Apple Dictionary",
grepl("com.aone.keka", app) ~ "Keka"
)
)

overall_usage %>%
slice(1:10) %>%
left_join(app_trans) %>%
mutate(name = fct_inorder(name) %>% fct_rev()) %>%
ggplot(aes(x=total, y=name)) +
geom_segment(aes(xend=0, yend=name), size=5, color = ft_cols$slate) +
scale_x_comma(position = "top") +
labs(
x = "Total Usage (hrs)", y = NULL,
title = glue::glue('App usage in the past {round(as.numeric(max(usage$end_time) - min(usage$start_time), "days"))} days')
) +
theme_ft_rc(grid = "X")

Next, top 10 App usage on my mac in the past 28 days:

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
usage %>%
dplyr::filter(app %in% overall_usage$app[1:10]) %>%
left_join(app_trans) %>%
mutate(
name = case_when(
grepl("com.apple.finder", app) ~ "Finder",
grepl("com.google.Chrome", app) ~ "Chrome",
grepl("com.tencent.xin", app) ~ "Wechat",
grepl("com.sublimetext.3", app) ~ "Sublime Text3",
grepl("com.netease.163music", app) ~ "Netease music",
grepl("com.apple.Terminal", app) ~ "Terminal",
grepl("org.rstudio.RStudio", app) ~ "RStudio",
grepl("com.youdao.YoudaoDict", app) ~ "Youdao Dict",
grepl("com.reederapp.macOS", app) ~ "Reeder",
grepl("com.adobe.Acrobat.Pro", app) ~ "Acrobat",
grepl("GMATPrep", app) ~ "GMAT",
grepl("com.weibo.international", app) ~ "Weibo",
grepl("com.apple.DigitalColorMeter", app) ~ "Digital Color Meter",
grepl("com.wutian.weibo", app) ~ "Weibo",
grepl("com.alipay.iphoneclient", app) ~ "Alipay",
grepl("org.julialang.launcherapp", app) ~ "Julia",
grepl("com.apple.quicktimeplayerx", app) ~ "Quick Time Player",
grepl("com.apple.archiveutility", app) ~ "Archive",
grepl("com.continuum.python", app) ~ "Python",
grepl("com.mathworks.matlab", app) ~ "MATLAB",
grepl("com.apple.Health", app) ~ "Apple Health",
grepl("com.apple.ScriptEditor2", app) ~ "Script Editor",
grepl("com.eltima.Folx3", app) ~ "Eltima",
grepl("com.apple.mobilecal", app) ~ "Apple Mobilecal",
grepl("com.apple.mobilephone", app) ~ "Mobile Phone",
grepl("com.anaconda.io", app) ~ "Anaconda",
grepl("youdaoPro", app) ~ "Youdao",
grepl("com.tencent.qqmail", app) ~ "QQ Mail",
grepl("com.aliyun.wstudio.amc.AliyunMobileApp", app) ~ "Aliyun Studio",
grepl("com.taobao.taobao4iphone", app) ~ "Taobao",
grepl("com.google.chrome.ios", app) ~ "Chrome",
grepl("reederapp", app) ~ "Reeder",
grepl("xunlei", app) ~ "Xunlei",
grepl("github", app) ~ "GitHub",
grepl("microsoft", app) ~ "Microsoft",
grepl("fournova", app) ~ "Fournova",
grepl("kugou", app) ~ "Kugou",
grepl("Preview", app) ~ "Preview",
grepl("Font", app) ~ "Font Book",
grepl("com.baidu.netdisk", app) ~ "Baidu Net Disk",
grepl("stata", app) ~ "Stata",
grepl("Kindle", app) ~ "Kindle",
grepl("Reader", app) ~ "Reader",
grepl("com.apple.TV", app) ~ "Apple TV",
grepl("mozilla", app) ~ "Firefox",
grepl("co.zeit.hyper", app) ~ "Zeit",
grepl("com.jetbrains.datagrip", app) ~ "DataGrip",
grepl("com.apple.QuickTimePlayerX", app) ~ "Quick Time Player",
grepl("pycharm", app) ~ "PyCharm",
grepl("Safari", app) ~ "Safari",
grepl("AppStore", app) ~ "AppStore",
grepl("TextEdit", app) ~ "Text Edit",
grepl("com.iqiyi.player", app) ~ "爱奇艺",
grepl("OmniGraffle", app) ~ "OmniGraffle",
grepl("com.baidu.BaiduNetdisk-mac", app) ~ "Baidu Net Disk",
grepl("com.apple.print.PrinterProxy", app) ~ "Printer",
grepl("com.apple.mail", app) ~ "Apple Mail",
grepl("com.apple.systempreferences", app) ~ "Apple System Preferences",
grepl("com.meitu.myxj", app) ~ "Meitu",
grepl("com.apple.findmy", app) ~ "Find My",
grepl("Alfred", app) ~ "Alfred",
grepl("com.jumsoft.ToolboxforPages", app) ~ "Tool Box for Pages",
grepl("com.apple.reminders", app) ~ "Apple Reminders",
grepl("com.apple.Music", app) ~ "Apple Music",
grepl("com.electron.painter", app) ~ "Painter",
grepl("net.java.openjdk.cmd", app) ~ "Java",
grepl("com.chaoui.jiongji100CN", app) ~ " Jiongji",
grepl("com.apple.Dictionary", app) ~ "Apple Dictionary",
grepl("com.aone.keka", app) ~ "Keka"
)
) %>%
ggplot() +
geom_segment(
aes(
x = start_time, xend = end_time, y = name, yend = name,
color = ifelse(start_dow %in% c("Saturday", "Sunday"), "Weekend", "Weekday")
),
size = 10,
) +
scale_x_datetime(position = "top") +
scale_colour_manual(
name = NULL,
values = c(
"Weekend" = ft_cols$light_blue,
"Weekday" = ft_cols$green
)
) +
guides(
colour = guide_legend(override.aes = list(size = 1))
) +
labs(
x = NULL, y = NULL,
title = glue::glue('Top 10 App usage on this Mac in the past {round(as.numeric(max(usage$end_time) - min(usage$start_time), "days"))} days'),
subtitle = "Each segment represents that app being 'up' (Open to Quit).\nUnfortunately, this is what Screen Time uses for its calculations on macOS"
) +
theme_ft_rc(grid="X") +
theme(legend.position = c(1, 1.25)) +
theme(legend.justification = "right")

The data source:

R
1
2
3
4
5
6
count(usage, source, wt = usage, sort = TRUE)
#> # A tibble: 2 x 2
#> source n
#> <chr> <int>
#> 1 Other 6423100
#> 2 iPhone8,1 312810

Top 10 App usage by day of week:

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
count(usage, start_dow, app, wt = usage/60/60) %>%
left_join(app_trans) %>%
dplyr::filter(app %in% overall_usage$app[1:10]) %>%
mutate(
name = case_when(
grepl("com.apple.finder", app) ~ "Finder",
grepl("com.google.Chrome", app) ~ "Chrome",
grepl("com.tencent.xin", app) ~ "Wechat",
grepl("com.sublimetext.3", app) ~ "Sublime Text3",
grepl("com.netease.163music", app) ~ "Netease music",
grepl("com.apple.Terminal", app) ~ "Terminal",
grepl("org.rstudio.RStudio", app) ~ "RStudio",
grepl("com.youdao.YoudaoDict", app) ~ "Youdao Dict",
grepl("com.reederapp.macOS", app) ~ "Reeder",
grepl("com.adobe.Acrobat.Pro", app) ~ "Acrobat",
grepl("GMATPrep", app) ~ "GMAT",
grepl("com.weibo.international", app) ~ "Weibo",
grepl("com.apple.DigitalColorMeter", app) ~ "Digital Color Meter",
grepl("com.wutian.weibo", app) ~ "Weibo",
grepl("com.alipay.iphoneclient", app) ~ "Alipay",
grepl("org.julialang.launcherapp", app) ~ "Julia",
grepl("com.apple.quicktimeplayerx", app) ~ "Quick Time Player",
grepl("com.apple.archiveutility", app) ~ "Archive",
grepl("com.continuum.python", app) ~ "Python",
grepl("com.mathworks.matlab", app) ~ "MATLAB",
grepl("com.apple.Health", app) ~ "Apple Health",
grepl("com.apple.ScriptEditor2", app) ~ "Script Editor",
grepl("com.eltima.Folx3", app) ~ "Eltima",
grepl("com.apple.mobilecal", app) ~ "Apple Mobilecal",
grepl("com.apple.mobilephone", app) ~ "Mobile Phone",
grepl("com.anaconda.io", app) ~ "Anaconda",
grepl("youdaoPro", app) ~ "Youdao",
grepl("com.tencent.qqmail", app) ~ "QQ Mail",
grepl("com.aliyun.wstudio.amc.AliyunMobileApp", app) ~ "Aliyun Studio",
grepl("com.taobao.taobao4iphone", app) ~ "Taobao",
grepl("com.google.chrome.ios", app) ~ "Chrome",
grepl("reederapp", app) ~ "Reeder",
grepl("xunlei", app) ~ "Xunlei",
grepl("github", app) ~ "GitHub",
grepl("microsoft", app) ~ "Microsoft",
grepl("fournova", app) ~ "Fournova",
grepl("kugou", app) ~ "Kugou",
grepl("Preview", app) ~ "Preview",
grepl("Font", app) ~ "Font Book",
grepl("com.baidu.netdisk", app) ~ "Baidu Net Disk",
grepl("stata", app) ~ "Stata",
grepl("Kindle", app) ~ "Kindle",
grepl("Reader", app) ~ "Reader",
grepl("com.apple.TV", app) ~ "Apple TV",
grepl("mozilla", app) ~ "Firefox",
grepl("co.zeit.hyper", app) ~ "Zeit",
grepl("com.jetbrains.datagrip", app) ~ "DataGrip",
grepl("com.apple.QuickTimePlayerX", app) ~ "Quick Time Player",
grepl("pycharm", app) ~ "PyCharm",
grepl("Safari", app) ~ "Safari",
grepl("AppStore", app) ~ "AppStore",
grepl("TextEdit", app) ~ "Text Edit",
grepl("com.iqiyi.player", app) ~ "爱奇艺",
grepl("OmniGraffle", app) ~ "OmniGraffle",
grepl("com.baidu.BaiduNetdisk-mac", app) ~ "Baidu Net Disk",
grepl("com.apple.print.PrinterProxy", app) ~ "Printer",
grepl("com.apple.mail", app) ~ "Apple Mail",
grepl("com.apple.systempreferences", app) ~ "Apple System Preferences",
grepl("com.meitu.myxj", app) ~ "Meitu",
grepl("com.apple.findmy", app) ~ "Find My",
grepl("Alfred", app) ~ "Alfred",
grepl("com.jumsoft.ToolboxforPages", app) ~ "Tool Box for Pages",
grepl("com.apple.reminders", app) ~ "Apple Reminders",
grepl("com.apple.Music", app) ~ "Apple Music",
grepl("com.electron.painter", app) ~ "Painter",
grepl("net.java.openjdk.cmd", app) ~ "Java",
grepl("com.chaoui.jiongji100CN", app) ~ " Jiongji",
grepl("com.apple.Dictionary", app) ~ "Apple Dictionary",
grepl("com.aone.keka", app) ~ "Keka"
)
) %>%
mutate(start_dow = factor(start_dow, c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"))) %>%
add_row(start_dow = "Wednesday", app = "id", n = 0, name = "Youdao Dict") %>%
ggplot() +
geom_tile(aes(start_dow, name, fill = n), color = "#252a32", size = 0.75) +
scale_x_discrete(expand = c(0, 0.5), position = "top") +
scale_y_discrete(expand = c(0, 0.5)) +
scale_fill_viridis_c(direction = -1, option = "magma", name = "Usage (hrs)") +
labs(
x = NULL, y = NULL,
title = "Top 10 App usage by day of week"
) +
theme_ft_rc(grid = "")

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
library(sys)
library(magick)
library(osqueryr) # notcran
# devtools::install_github('hrbrmstr/mactheknife')
library(mactheknife) #notcran
library(ggimage)
library(hrbrthemes)
library(ggbeeswarm)

osq_expose_tables("apps")
default_app <- "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns"
select(apps, name, path, last_opened_time) %>%
collect() %>%
dplyr::filter(!str_detect(path, "(^/System|usr|//System|/Library/|Helper|/Contents/|\\.service$)")) %>%
mutate(lop_day = as.Date(anytime::anytime(as.numeric(last_opened_time)))) %>%
mutate(icon = map_chr(path, ~{
p <- read_plist(file.path(.x, "Contents", "Info.plist"))
icns <- p$CFBundleIconFile[1]
if (is.null(icns)) return(default_app)
if (!str_detect(icns, "\\.icns$")) icns <- sprintf("%s.icns", icns)
file.path(.x, "Contents", "Resources", icns)
})) -> apps_df

The shell command icns2png can be installed by brew install libicns.

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
cache_dir <- path.expand("~/.r-icns-cache")
dir.create(cache_dir)

# create a unique name hash for more compact names
mutate(apps_df, icns_png = map_chr(icon, ~{
hash <- digest::digest(.x, serialize=FALSE)
file.path(cache_dir, sprintf("%s.png", hash))
})) -> apps_df

# find the icns2png program
# brew install libicns
icns2png <- unname(Sys.which("icns2png"))

# go through each icon file
pb <- progress_estimated(length(apps_df$icns_png))
walk2(apps_df$icon, apps_df$icns_png, ~{
pb$tick()$print() # progress!
if (!file.exists(.y)) { # don't create it if it already exists
td <- tempdir()
# no icon file == use default one
if (!file.exists(.x)) .x <- default_app
# convert all of them to pngs
sys::exec_internal(
cmd = icns2png,
args = c("-x", "-o", td, .x),
error = FALSE
) -> res
rawToChar(res$stdout) %>% # go through icns2png output
str_split("\n") %>%
flatten_chr() %>%
keep(str_detect, " Saved") %>% # find all the extracted icons
last() %>% # use the last one
str_replace(".* to /", "/") %>% # clean up the filename so we can read it in
str_replace("\\.$", "") -> png
# read and convert
try(image_read(png) %>%
image_resize(geometry_area(64, 64)) %>%
image_write(.y))
}
})

dplyr::filter(apps_df, lop_day > as.Date("2019-01-01")) %>%
ggplot() +
geom_image(
aes(x = "", lop_day, image = icns_png), size = 0.033,
position = position_quasirandom(width = 0.5)
) +
geom_text(
data = data_frame(
x = c(0.6, 0.6),
y = as.Date(c("2019-09-01", "2019-06-15")),
label = c("More recently used ↑", "Not used in a while ↓")
),
aes(x, y, label = label), family = font_an, size = 5 , hjust = 0,
color = "lightslategray"
) +
labs(x = NULL, y = "Last Opened Time") +
labs(
x = NULL, y = NULL,
title = "Mac OS 'Last Used' App History"
) +
theme_ipsum_rc(grid="Y") +
theme(axis.text.x = element_blank()) +
scale_y_date(breaks = as.Date(c("2019-05-01", "2019-06-01", "2019-07-01", "2019-08-01", "2019-09-01", "2019-10-01", "2019-11-01")))

# R

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×