library(highcharter)
d1 = read_csv("tech_stocks_csv.zip", show_col_types = FALSE) %>%
filter(sale > 0) %>%
mutate(conm = stringr::str_to_title(conm), # Converts the string to title case
datadate = lubridate::ymd(datadate)) # Convert datadate into a date variable
I will also set the Highcharter theme for all the plots
options(highcharter.theme = hc_theme_darkunica())
Q1
Note that while creating d1_1, I arranged it in the end
so that we will get ordered bar plot.
d1_1 = d1 %>%
group_by(conm) %>%
summarize(avg_sale = mean(sale), .groups = "drop") %>%
arrange(-avg_sale)
hchart(d1_1, "column", hcaes(x = conm, y = round(avg_sale, 2))) %>%
hc_xAxis(title = list(text = "Company")) %>%
hc_yAxis(title = list(text = "Average Sale in $ Millions"),
labels = list(format = "${value:,.0f}")) %>% # I used ChatGPT to find out how to create this format
hc_chart(inverted = TRUE)
Q3
Here I will first create a scatterplot. Adding a regression line is a
little more complex so I will add that below.
Also, I am rounding the numbers in the calculated ratios.
d1_ratio = d1 %>%
mutate(debt_ratio = round((dlc + replace_na(dltt, 0)) / at, 3),
rnd_int = round(xrd / sale, 3))
d1_ratio %>%
hchart("scatter", hcaes(x = debt_ratio, y = rnd_int)) %>%
hc_xAxis(title = list(text = "Debt Ratio")) %>%
hc_yAxis(title = list(text = "R&D to Sales Ratio"))
Now let’s add a regression line. There is no direct way to add a
regression line easily. We must estimate the regression model first and
then use that model to get predicted values. I am using a super useful
package in R called broom which is a part of
tidyverse to convert the regression model into a data frame
of predicted values.
library(broom)
reg = lm(rnd_int ~ debt_ratio, data = d1_ratio)
reg_fit = augment(reg) %>% arrange(debt_ratio)
augment function from broom package adds
predicted values from the regression and other statistics back to the
original data frame, which is d1_raio in our case. Take a
peek at the resulting data frame:
head(reg_fit)
Here, we are intereted in the variable called .fitted
which has predicted values of rnd_int based on
debt_ratio. I arranged the data frame by
debt_ratio so that highcharter can simply superimpose the
regression line on our scatterplot.
d1_ratio %>%
hchart(type = "scatter", hcaes(x = debt_ratio, y = rnd_int)) %>%
hc_add_series(data = reg_fit, type = "line", hcaes(x = debt_ratio, y = .fitted)) %>%
hc_xAxis(title = list(text = "Debt Ratio")) %>%
hc_yAxis(title = list(text = "R&D to Sales Ratio"))
Q6
Creating a face plot is also not straightforward. Note that a
facetplot makes sense for a static visualization. In internactive
visualization, you could place multiple charts side by side on a webpage
easily. You may also provide users a dropdown to select the charts they
want to see.
Anyway, I will first show you an alterative to the faceplot in Q6
where we are comparing only 2 companies.
Note that I have changed the tooltip quite a bit. Try to understand
this code. The last function in the code adds the labels to the
bars.
d1 %>%
filter(tic %in% c("META", "NVDA")) %>%
filter(fyear >= 2010) %>%
hchart("column", hcaes(x = as.character(fyear), y = oibdp, group = conm, name = as.character(fyear))) %>%
hc_colors(c("#5cc9f5", "#b131a2")) %>%
hc_xAxis(title = list(text = "Fiscal Year")) %>%
hc_yAxis(title = list(text = "Profits in $ million")) %>%
hc_legend(align = "center", verticalAlign = "top", layout = "horizontal", symbolRadius = 0) %>%
hc_tooltip(headerFormat = "",
pointFormat = 'Year: {point.name}<br><span style="color:{point.color}">\u2714</span> {series.name}: <b>{point.y}</b><br/>') %>%
hc_plotOptions(series = list(dataLabels = list(enabled = TRUE, format = "{y}")))
NA
Back to creating a facet plot. The solution is to create two separate
plots and then arrange them using an obscure fuction called
hw_grid() in highcharter. This function is not ported from
the JS library. Instead the developer wrote this for R users.
Anyway, the method is to create separate plots for Meta and nvidia
and then put them in one column. To save typing, I used map
function from purrr package to apply tickers individually
to the same plotting function. It creates 2 plots and saves them as a
list named charts. Next, I use hw_grid to
organize these two vertically. In other words, we can’t create
facetplots as easily as we did in ggplot2.
tickers = c("META", "NVDA")
colors = list(META = "#5cc9f5", NVDA = "#b131a2")
charts <- map(tickers, function(x) {
.dt = d1 %>% filter(tic == x)
hchart(.dt,
type = "column",
hcaes(x = as.character(fyear), y = oibdp, name = fyear)) %>%
hc_colors(colors[[x]]) %>%
hc_xAxis(title = list(text = "Fiscal Year")) %>%
hc_yAxis(title = list(text = "Profits in $ million")) %>%
hc_title(text = .dt$conm[1]) %>%
hc_legend(enabled = FALSE) %>%
hc_tooltip(headerFormat = "",
pointFormat = 'Year: {point.name}<br><span style="color:{point.color}">\u2714</span> {series.name}: <b>{point.y}</b><br/>') %>%
hc_plotOptions(series = list(dataLabels = list(enabled = TRUE, format = "{y}")))
})
hw_grid(charts, ncol = 1, rowheight = 600)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDoga2FibGUKICAgIHRoZW1lOiB5ZXRpCiAgICB0b2M6IHllcwotLS0KCmBgYHtyfQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQpgYGAKCgpgYGB7ciBlY2hvPVRSVUV9CmQxID0gcmVhZF9jc3YoInRlY2hfc3RvY2tzX2Nzdi56aXAiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUgCiAgZmlsdGVyKHNhbGUgPiAwKSAlPiUgCiAgbXV0YXRlKGNvbm0gPSBzdHJpbmdyOjpzdHJfdG9fdGl0bGUoY29ubSksICMgQ29udmVydHMgdGhlIHN0cmluZyB0byB0aXRsZSBjYXNlCiAgICAgICAgIGRhdGFkYXRlID0gbHVicmlkYXRlOjp5bWQoZGF0YWRhdGUpKSAjIENvbnZlcnQgZGF0YWRhdGUgaW50byBhIGRhdGUgdmFyaWFibGUKYGBgCgpJIHdpbGwgYWxzbyBzZXQgdGhlIEhpZ2hjaGFydGVyIHRoZW1lIGZvciBhbGwgdGhlIHBsb3RzCgpgYGB7cn0Kb3B0aW9ucyhoaWdoY2hhcnRlci50aGVtZSA9IGhjX3RoZW1lX2Rhcmt1bmljYSgpKQpgYGAKCgojIyBRMSAKCgpOb3RlIHRoYXQgd2hpbGUgY3JlYXRpbmcgYGQxXzFgLCBJIGFycmFuZ2VkIGl0IGluIHRoZSBlbmQgc28gdGhhdCB3ZSB3aWxsIGdldCBvcmRlcmVkIGJhciBwbG90LgoKYGBge3IgZWNobz1UUlVFfQpkMV8xID0gZDEgJT4lCiAgZ3JvdXBfYnkoY29ubSkgJT4lIAogIHN1bW1hcml6ZShhdmdfc2FsZSA9IG1lYW4oc2FsZSksIC5ncm91cHMgPSAiZHJvcCIpICU+JSAKICBhcnJhbmdlKC1hdmdfc2FsZSkKYGBgCgpgYGB7cn0KaGNoYXJ0KGQxXzEsICJjb2x1bW4iLCBoY2Flcyh4ID0gY29ubSwgeSA9IHJvdW5kKGF2Z19zYWxlLCAyKSkpICU+JQogIGhjX3hBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIkNvbXBhbnkiKSkgJT4lCiAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiQXZlcmFnZSBTYWxlIGluICQgTWlsbGlvbnMiKSwgCiAgICAgICAgICAgbGFiZWxzID0gbGlzdChmb3JtYXQgPSAiJHt2YWx1ZTosLjBmfSIpKSAlPiUgIyBJIHVzZWQgQ2hhdEdQVCB0byBmaW5kIG91dCBob3cgdG8gY3JlYXRlIHRoaXMgZm9ybWF0CiAgaGNfY2hhcnQoaW52ZXJ0ZWQgPSBUUlVFKQpgYGAKCgojIyBRMwoKCkhlcmUgSSB3aWxsIGZpcnN0IGNyZWF0ZSBhIHNjYXR0ZXJwbG90LiBBZGRpbmcgYSByZWdyZXNzaW9uIGxpbmUgaXMgYSBsaXR0bGUgbW9yZSBjb21wbGV4IHNvIEkgd2lsbCBhZGQgdGhhdCBiZWxvdy4KCkFsc28sIEkgYW0gcm91bmRpbmcgdGhlIG51bWJlcnMgaW4gdGhlIGNhbGN1bGF0ZWQgcmF0aW9zLgoKYGBge3J9CmQxX3JhdGlvID0gZDEgJT4lIAogIG11dGF0ZShkZWJ0X3JhdGlvID0gcm91bmQoKGRsYyArIHJlcGxhY2VfbmEoZGx0dCwgMCkpIC8gYXQsIDMpLAogICAgICAgICBybmRfaW50ID0gcm91bmQoeHJkIC8gc2FsZSwgMykpCgpkMV9yYXRpbyAlPiUgCmhjaGFydCgic2NhdHRlciIsIGhjYWVzKHggPSBkZWJ0X3JhdGlvLCB5ID0gcm5kX2ludCkpICU+JSAKICBoY194QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJEZWJ0IFJhdGlvIikpICU+JQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIlImRCB0byBTYWxlcyBSYXRpbyIpKQpgYGAKCk5vdyBsZXQncyBhZGQgYSByZWdyZXNzaW9uIGxpbmUuIFRoZXJlIGlzIG5vIGRpcmVjdCB3YXkgdG8gYWRkIGEgcmVncmVzc2lvbiBsaW5lIGVhc2lseS4gV2UgbXVzdCBlc3RpbWF0ZSB0aGUgcmVncmVzc2lvbiBtb2RlbCBmaXJzdCBhbmQgdGhlbiB1c2UgdGhhdCBtb2RlbCB0byBnZXQgcHJlZGljdGVkIHZhbHVlcy4gSSBhbSB1c2luZyBhIHN1cGVyIHVzZWZ1bCBwYWNrYWdlIGluIFIgY2FsbGVkIGBicm9vbWAgd2hpY2ggaXMgYSBwYXJ0IG9mIGB0aWR5dmVyc2VgIHRvIGNvbnZlcnQgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgaW50byBhIGRhdGEgZnJhbWUgb2YgcHJlZGljdGVkIHZhbHVlcy4KCmBgYHtyfQpsaWJyYXJ5KGJyb29tKQpyZWcgPSBsbShybmRfaW50IH4gZGVidF9yYXRpbywgZGF0YSA9IGQxX3JhdGlvKQpyZWdfZml0ID0gYXVnbWVudChyZWcpICU+JSBhcnJhbmdlKGRlYnRfcmF0aW8pCmBgYAoKYGF1Z21lbnRgIGZ1bmN0aW9uIGZyb20gYGJyb29tYCBwYWNrYWdlIGFkZHMgcHJlZGljdGVkIHZhbHVlcyBmcm9tIHRoZSByZWdyZXNzaW9uIGFuZCBvdGhlciBzdGF0aXN0aWNzIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUsIHdoaWNoIGlzIGBkMV9yYWlvYCBpbiBvdXIgY2FzZS4gVGFrZSBhIHBlZWsgYXQgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lOgoKYGBge3J9CmhlYWQocmVnX2ZpdCkKYGBgCkhlcmUsIHdlIGFyZSBpbnRlcmV0ZWQgaW4gdGhlIHZhcmlhYmxlIGNhbGxlZCBgLmZpdHRlZGAgd2hpY2ggaGFzIHByZWRpY3RlZCB2YWx1ZXMgb2YgYHJuZF9pbnRgIGJhc2VkIG9uIGBkZWJ0X3JhdGlvYC4gSSBhcnJhbmdlZCB0aGUgZGF0YSBmcmFtZSBieSBgZGVidF9yYXRpb2Agc28gdGhhdCBoaWdoY2hhcnRlciBjYW4gc2ltcGx5IHN1cGVyaW1wb3NlIHRoZSByZWdyZXNzaW9uIGxpbmUgb24gb3VyIHNjYXR0ZXJwbG90LgoKCmBgYHtyfQpkMV9yYXRpbyAlPiUgCiAgaGNoYXJ0KHR5cGUgPSAic2NhdHRlciIsIGhjYWVzKHggPSBkZWJ0X3JhdGlvLCB5ID0gcm5kX2ludCkpICU+JSAKICBoY19hZGRfc2VyaWVzKGRhdGEgPSByZWdfZml0LCB0eXBlID0gImxpbmUiLCBoY2Flcyh4ID0gZGVidF9yYXRpbywgeSA9IC5maXR0ZWQpKSAlPiUKICBoY194QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJEZWJ0IFJhdGlvIikpICU+JQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIlImRCB0byBTYWxlcyBSYXRpbyIpKQpgYGAKCiMjIFE2CgpDcmVhdGluZyBhIGZhY2UgcGxvdCBpcyBhbHNvIG5vdCBzdHJhaWdodGZvcndhcmQuIE5vdGUgdGhhdCBhIGZhY2V0cGxvdCBtYWtlcyBzZW5zZSBmb3IgYSBzdGF0aWMgdmlzdWFsaXphdGlvbi4gSW4gaW50ZXJuYWN0aXZlIHZpc3VhbGl6YXRpb24sIHlvdSBjb3VsZCBwbGFjZSBtdWx0aXBsZSBjaGFydHMgc2lkZSBieSBzaWRlIG9uIGEgd2VicGFnZSBlYXNpbHkuIFlvdSBtYXkgYWxzbyBwcm92aWRlIHVzZXJzIGEgZHJvcGRvd24gdG8gc2VsZWN0IHRoZSBjaGFydHMgdGhleSB3YW50IHRvIHNlZS4KCkFueXdheSwgSSB3aWxsIGZpcnN0IHNob3cgeW91IGFuIGFsdGVyYXRpdmUgdG8gdGhlIGZhY2VwbG90IGluIFE2IHdoZXJlIHdlIGFyZSBjb21wYXJpbmcgb25seSAyIGNvbXBhbmllcy4KCk5vdGUgdGhhdCBJIGhhdmUgY2hhbmdlZCB0aGUgdG9vbHRpcCBxdWl0ZSBhIGJpdC4gVHJ5IHRvIHVuZGVyc3RhbmQgdGhpcyBjb2RlLiBUaGUgbGFzdCBmdW5jdGlvbiBpbiB0aGUgY29kZSBhZGRzIHRoZSBsYWJlbHMgdG8gdGhlIGJhcnMuIAoKYGBge3J9CgpkMSAlPiUgCiAgZmlsdGVyKHRpYyAlaW4lIGMoIk1FVEEiLCAiTlZEQSIpKSAlPiUgCiAgZmlsdGVyKGZ5ZWFyID49IDIwMTApICU+JSAKICBoY2hhcnQoImNvbHVtbiIsIGhjYWVzKHggPSBhcy5jaGFyYWN0ZXIoZnllYXIpLCB5ID0gb2liZHAsIGdyb3VwID0gY29ubSwgbmFtZSA9IGFzLmNoYXJhY3RlcihmeWVhcikpKSAlPiUKICBoY19jb2xvcnMoYygiIzVjYzlmNSIsICIjYjEzMWEyIikpICU+JQogIGhjX3hBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIkZpc2NhbCBZZWFyIikpICU+JQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIlByb2ZpdHMgaW4gJCBtaWxsaW9uIikpICU+JQogIGhjX2xlZ2VuZChhbGlnbiA9ICJjZW50ZXIiLCB2ZXJ0aWNhbEFsaWduID0gInRvcCIsIGxheW91dCA9ICJob3Jpem9udGFsIiwgc3ltYm9sUmFkaXVzID0gMCkgJT4lCiAgaGNfdG9vbHRpcChoZWFkZXJGb3JtYXQgPSAiIiwKICAgICAgICAgICAgIHBvaW50Rm9ybWF0ID0gJ1llYXI6IHtwb2ludC5uYW1lfTxicj48c3BhbiBzdHlsZT0iY29sb3I6e3BvaW50LmNvbG9yfSI+XHUyNzE0PC9zcGFuPiB7c2VyaWVzLm5hbWV9OiA8Yj57cG9pbnQueX08L2I+PGJyLz4nKSAlPiUKICBoY19wbG90T3B0aW9ucyhzZXJpZXMgPSBsaXN0KGRhdGFMYWJlbHMgPSBsaXN0KGVuYWJsZWQgPSBUUlVFLCBmb3JtYXQgPSAie3l9IikpKQoKYGBgCgoKQmFjayB0byBjcmVhdGluZyBhIGZhY2V0IHBsb3QuIFRoZSBzb2x1dGlvbiBpcyB0byBjcmVhdGUgdHdvIHNlcGFyYXRlIHBsb3RzIGFuZCB0aGVuIGFycmFuZ2UgdGhlbSB1c2luZyBhbiBvYnNjdXJlIGZ1Y3Rpb24gY2FsbGVkIGBod19ncmlkKClgIGluIGhpZ2hjaGFydGVyLiBUaGlzIGZ1bmN0aW9uIGlzIG5vdCBwb3J0ZWQgZnJvbSB0aGUgSlMgbGlicmFyeS4gSW5zdGVhZCB0aGUgZGV2ZWxvcGVyIHdyb3RlIHRoaXMgZm9yIFIgdXNlcnMuCgpBbnl3YXksIHRoZSBtZXRob2QgaXMgdG8gY3JlYXRlIHNlcGFyYXRlIHBsb3RzIGZvciBNZXRhIGFuZCBudmlkaWEgYW5kIHRoZW4gcHV0IHRoZW0gaW4gb25lIGNvbHVtbi4gVG8gc2F2ZSB0eXBpbmcsIEkgdXNlZCBgbWFwYCBmdW5jdGlvbiBmcm9tIGBwdXJycmAgcGFja2FnZSB0byBhcHBseSB0aWNrZXJzIGluZGl2aWR1YWxseSB0byB0aGUgc2FtZSBwbG90dGluZyBmdW5jdGlvbi4gSXQgY3JlYXRlcyAyIHBsb3RzIGFuZCBzYXZlcyB0aGVtIGFzIGEgbGlzdCBuYW1lZCBgY2hhcnRzYC4gTmV4dCwgSSB1c2UgYGh3X2dyaWRgIHRvIG9yZ2FuaXplIHRoZXNlIHR3byB2ZXJ0aWNhbGx5LiBJbiBvdGhlciB3b3Jkcywgd2UgY2FuJ3QgY3JlYXRlIGZhY2V0cGxvdHMgYXMgZWFzaWx5IGFzIHdlIGRpZCBpbiBnZ3Bsb3QyLgoKCmBgYHtyfQoKdGlja2VycyA9IGMoIk1FVEEiLCAiTlZEQSIpCmNvbG9ycyA9IGxpc3QoTUVUQSA9ICIjNWNjOWY1IiwgTlZEQSA9ICIjYjEzMWEyIikKCmNoYXJ0cyA8LSBtYXAodGlja2VycywgZnVuY3Rpb24oeCkgewogIAogIC5kdCA9IGQxICU+JSBmaWx0ZXIodGljID09IHgpCiAgCiAgaGNoYXJ0KC5kdCwgCiAgICAgICAgIHR5cGUgPSAiY29sdW1uIiwgCiAgICAgICAgIGhjYWVzKHggPSBhcy5jaGFyYWN0ZXIoZnllYXIpLCB5ID0gb2liZHAsIG5hbWUgPSBmeWVhcikpICAgJT4lCiAgICBoY19jb2xvcnMoY29sb3JzW1t4XV0pICU+JQogICAgaGNfeEF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiRmlzY2FsIFllYXIiKSkgJT4lCiAgICBoY195QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJQcm9maXRzIGluICQgbWlsbGlvbiIpKSAlPiUKICAgIGhjX3RpdGxlKHRleHQgPSAuZHQkY29ubVsxXSkgJT4lCiAgICBoY19sZWdlbmQoZW5hYmxlZCA9IEZBTFNFKSAlPiUKICAgIGhjX3Rvb2x0aXAoaGVhZGVyRm9ybWF0ID0gIiIsCiAgICAgICAgICAgICAgIHBvaW50Rm9ybWF0ID0gJ1llYXI6IHtwb2ludC5uYW1lfTxicj48c3BhbiBzdHlsZT0iY29sb3I6e3BvaW50LmNvbG9yfSI+XHUyNzE0PC9zcGFuPiB7c2VyaWVzLm5hbWV9OiA8Yj57cG9pbnQueX08L2I+PGJyLz4nKSAlPiUKICAgIGhjX3Bsb3RPcHRpb25zKHNlcmllcyA9IGxpc3QoZGF0YUxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIGZvcm1hdCA9ICJ7eX0iKSkpCn0pCgpgYGAKCmBgYHtyfQpod19ncmlkKGNoYXJ0cywgbmNvbCA9IDEsIHJvd2hlaWdodCA9IDYwMCkKYGBgCgoKCgo=