I discovered "How To Design Programs" somewhere late in my first year of using R. Like most beginning R coders with nominal experience in other languages, I wrote a lot of monolithic scripts in a very imperative style. HtDP gave me a mental framework for decomposing larger problems into bite-sized chunks. The lispy roots of R lent itself particularly well to the model of thinking presented in that book.
Ever since then, I've pined for the graphing calculator parts in a more modern Scheme. When ggplot and then the tidyverse (neé hadleyverse) came on the scene, I was even more convinced that Scheme, especially Racket, was the ideal future for data science. If R could support a large ecosystem like tidyverse, just imagine what the metaprogramming facilities of Racket could do!
But I think those graphing calculator parts are hard to reproduce. Attempts to clone ggplot2 fall short year after year, because most other languages don't have grid graphics to build on top of. R is a deep ecosystem on "an OK scheme," which is damned hard to beat.
Aside: my first year with R, was in an urban planning masters program and I was terrified of my first big kid statistics course (taught in SPSS). I decided I'd give myself bonus work by learning R. While it was absurd to be doing my stats homework in SPSS, then R, then reviewing HtDP on top of the rest of my course load, I did ace that stats course. :-)
My first reaction, is "why not on a modern Scheme as opposed to Common Lisp" and in so thinking, I have demonstrated exactly why no lisp / scheme has ever achieved critical mass :-)
This hits home for me. We are just starting to use R for risk modeling where I work. R, more than any language I've ever used, makes me appreciate "worse is better". From a theoretical "aesthetic" perspective R is a mess. Yet for data processing all those theoretical concerns don't matter. It just works.
It's honestly kind of humbling that something so theoretically messy can be so practically coherent. It makes me question my assumptions about simplicity.
That said I still run into trouble with package deprecations. I was trying to install the optmatch package (deprecated but still used by causal inference packages) and had a really tough time getting it to compile on macOS.
I think R 3.0 introduced namespaces which fixed a lot of the really crazy stuff.
Also, I was writing Sweave in 2010 for my thesis, and I definitely wasn't alone.
When I think back to the era you're describing what I recall was people winging around hacky scripts being the norm regardless of their environment. While still not something I'd think of as software engineering best practices, what I see now is less Wild West.
Really there wasn't a lot of thought put into the language. We figure that if it ends up being a total failure, we can just pivot.
library(tidyverse)
library(scales)
download.file(url = "https://api.coronavirus.data.gov.uk/v2/data?areaType=overview&metric=covidOccupiedMVBeds&metric=newAdmissions&metric=newCasesBySpecimenDate&metric=newDeaths28DaysByDeathDate&metric=newPeopleReceivingFirstDose&format=csv", destfile = "./data.csv", method = "wget")
read_csv("./data.csv") %>%
pivot_longer(names_to = "Data", cols = c(newCasesBySpecimenDate,
covidOccupiedMVBeds,
newAdmissions,
newDeaths28DaysByDeathDate)) %>%
mutate(Data = factor(Data)) %>%
mutate(Data = recode_factor(Data, newCasesBySpecimenDate = "New Cases",
newAdmissions = "Admissions",
newDeaths28DaysByDeathDate = "Deaths",
covidOccupiedMVBeds = "Ventilated")) %>%
ggplot(aes(y = value, x = date, colour = Data))+
geom_point(size = 1, colour = "gray", alpha = 0.6)+
geom_smooth(type = "LOESS", span = 0.1)+
labs(y = "Daily rate", x = "Date", colour = "UK COVID-19")+
scale_x_date(date_breaks = "months", date_labels = "%b-%y")+
scale_y_log10(labels = comma(10 ^ (0:5),
accuracy = 1),
breaks = 10 ^ (0:5))+
theme(axis.text.x = element_text(angle = 45, hjust = 1)) import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sn
df = (pd.read_csv("/tmp/overview_2022-03-21.csv") # i just used curl beforehand
.assign(date=lambda x: pd.to_datetime(x["date"]))
.set_index("date")
.melt(value_vars=[
"newCasesBySpecimenDate",
"covidOccupiedMVBeds",
"newAdmissions",
"newDeaths28DaysByDeathDate"],
var_name="Data", ignore_index=False)
.assign(Data=lambda x: x["Data"].replace({
"newCasesBySpecimenDate": "New Cases",
"newAdmissions": "Admissions",
"newDeaths28DaysByDeathDate": "Deaths",
"covidOccupiedMVBeds": "Ventilated"
}))
)
ax = sn.scatterplot(data=df, x=df.index, y=df["value"], hue="Data")
ax.set(xlabel="Date", ylabel="Daily rate", yscale="log")
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b"))
plt.show()
I spend 2 minutes on the pandas part and 20 minutes on the plotting part, which really says it all. Seaborn's support for smoothing is really bad and doesn't play nicely with datetimes for some reason, so if I wanted smoothing I'd need to do it myself. And the other stuff I left out requires going into matplotlib's documentation which I don't want to spend time on.pandas is as good or better than R's dataframe manipulation, but R's plotting tools are best in class. I hate all the python plotting libraries.
I've given up discussing this. My personal opinion is that MPL is the assembly vs ggplots2's Python.
Yes, maybe there are some things that are doable in MPL but not in ggplot2, but never (and I mean - never -) has any one of my colleagues found a single example that I couldnt recreate in ggplot2 with a more readable code that resulted in a better looking plot.
MPL has this Latex like "once you get the hang of it, you will never use anything else in life for anything" and OOP's "If its good code then it is OOP and if its not, then you're not doing correct OOP" mythisque hanging over it. When confronted with bad MPL code that results in bad plots, its always one of those two.
ggplot2 is the best plotting package out there and imo one of the best "end-user" packages in any language. Also, Hadley is a saint.
You don't need the curl -- read_csv works with URLs directly.
The lambda can be replaced by passing parse_dates=[“date”]
It revealed to me that there's a buglet in `forcats::last()` (https://github.com/tidyverse/forcats/issues/303) and made me wonder if `pivot_longer()` should be able to rename the columns as you pivot them (https://github.com/tidyverse/tidyr/issues/1338)
Renaming factors is one of those things that always seems a bit awkward. I think I've used several methods. Passing a list of named vectors into `levels(x)` allows a many-to-one mapping but was quite dangerous. I've used revalue and mapvalues from plyr. fct_recode is new to me. But yes, renaming while reshaping could be quite convenient. Just looking at fct_recode now, it looks really nice. Seems to support many-to-one and being able to pass it a name vector is very convenient.
Learnt so much today, and this is even before trying out the python and julia examples! Many thanks for this and all your work in R!
import CSV
using Chain: @chain
using DataFrames
import Downloads
using Gadfly
using Dates
@chain begin
Downloads.download(
"https://api.coronavirus.data.gov.uk/v2/data?areaType=overview&metric=covidOccupiedMVBeds&metric=newAdmissions&metric=newCasesBySpecimenDate&metric=newDeaths28DaysByDeathDate&metric=newPeopleReceivingFirstDose&format=csv",
)
CSV.File
DataFrame
stack(
[:newCasesBySpecimenDate, :covidOccupiedMVBeds, :newAdmissions, :newDeaths28DaysByDeathDate];
variable_name = :Data,
)
transform(
:Data =>
(
x -> replace(
x,
"newCasesBySpecimenDate" => "NewCases",
"newAdmissions" => "Admissions",
"newDeaths28DaysByDeathDate" => "Deaths",
"covidOccupiedMVBeds" => "Ventilated",
)
) => :Data,
)
subset(:value => ByRow(!ismissing)) # Can't plot Geom.smooth with missings
plot(
_,
x = :date,
y = :value,
colour = :Data,
layer(Geom.smooth(method = :loess, smoothing = 0.1)),
layer(Geom.point),
Scale.y_log10(),
Guide.xlabel("Date"),
Guide.ylabel("Daily rate"),
Guide.xlabel("Angle"),
Guide.colorkey(title = "UK COVID-19"),
)
endUnfortunately, it’s also a lot of work. In this case, you’ve posted an intermediate stage artifact from R. If one of the many Python programmers reading this want to produce a comparable artifact they need to understand or run that code. That alone reduces your likelihood of getting any substantial replies.
Maybe add a link to an image of the resulting plot?
Yes, I love these things and very curious to see what hackernews comes up with. Project Euler was an eye opener for how things could be optimised in different languages.
My skin writhes every time I need to type:
table.loc[(table.column > 2) | (table.column2 < 3)].reset_index(drop=True)
when I want to subset a table.
table.loc[lambda df: df["column"].between(2, 3, inclusive="neither")]
this is useful when your dataframe has a long name, or when you have some long method chain and you need to subset at the end: table.foo().bar().baz().loc[lambda df: ...]it is still more verbose, but I actually prefer always providing column names as strings. it's more explicit. I don't like R's environment-manipulation metaprogramming magic where you can give column names as symbols.
as for resetting the index all the time, this is something of an antipattern. if you set up your index right beforehand it isn't necessary so often.
`table.query("column > 2 and column2 < 3")`
0: https://marketplace.visualstudio.com/items?itemName=ms-tools...
If you know the class of an object, let's say Class, but the object has yet to be "constructed" so that IPython can correctly infer its type, you can type `Class.[TAB]` in IPython and look at its methods.
For example, in Sympy, you have a matrix type called Matrix. You can do `(A * B).diagonalize()`, or alternatively you can do `Matrix.diagonalize(A * B)`, which has some advantages because doing `(A * B).[TAB]` does nothing useful because Python can't infer types.
You can also do the same for modules. `ModuleName.[TAB]`
To be honest though, I found the experience smoother in R for some reason.
I would say the most frustrating part about RStudio is that it is a workbook where you can execute code based on your cursor. For my wife, these workbooks become a total mess because things aren't necessarily run sequentially.
1. Always run from the top, using the "run previous chunks" button. When this gets too slow, you know that it's time to think harder about your workflow. For a more extreme version of the same idea, regularly restart R using Ctrl-Shift-0, and run from the top. It'll ensure your code is working right.
2. Have a setup chunk that always gets you to the same state. Make sure every other chunk works directly after calling the setup chunk. Then just alternate between "run setup chunk" and "run current chunk".
> the question is how much code you should write in the notebook, versus having it in a more organized set of functions and libraries. It's very easy to end up with a huge bloated document which contains thousands of lines of spaghetti.
But this isn't correct:
> your options are more or less use a notebook, or copy and paste your results.
What's wrong with writing scripts that write images to disk? That's how millions of academic papers were written before the advent of notebooks. You could use Makefiles if you like, or you could even use a technology such as Sweave to automatically mix images with LaTeX output.
I mean this as politely as possible but the fact that you think that the options are "use a notebook or copy and paste" I think shows that you've caught a notebook mentality disease! The fundamental point I'm trying to make is that you we don't need to do everything interactively from REPLs. REPLs are great for trying things out, but when it comes to producing the images for your paper, those should be produced by scripts, not by commands entered into a REPL, or notebook. An those scripts should evolve via version control, which is the basis of evolving any good and correct software. And the scripts for producing images for a paper should be good and correct software.
The point is that it makes sense to mix english prose + code to e.g. produce tables or graphs, even if most of the heavy lifting is done separately in code files.
Yep, so what you say makes sense. Isn't it sometimes a bit overly prescriptive to assume all collaborators use Rmarkdown? (Perhaps not! I used to work in biology and statistics and R was very ubiquitous.)
To which I mean R is a highly optimized, well-oiled machine if you're using it for its highly-optimized, well-oiled purposes. I tend to have notebooks full of tiny fragments like this
It's madness if you're not familiar with the tidyverse, but 3 dozen fragments like this is enough to eviscerate a fresh data set. Almost any question you can dream of is a 3-20 line set of transforms away from a beautiful plot or analysis answering your question. Very notably, this includes some of the finest modeling tools available today.Terseness here is a huge advantage as well because in many data analysis workflows you are rerunning that same 10 line snippet over and over, making small changes, adjusting to eventually visualize the thing you're looking for perfectly. Having all of that in the same small block is ideal.
Finally, for the non-trivial number of folks in this specific scenario, the integration between Stan and R/RStudio is top-notch and makes using both tools very pleasant.
You can replicate all of this in Python, but optimal Python/Jupyter is still a far cry away from R/RStudio for these specific sorts of tasks.