A guest post by Jeff Hemsley, who has co-authored with Karine Nahon a new book titled Going Viral. ————————-
In Going Viral (Polity Press, 2013) we explore the topic of virality, the process of sharing messages that results in a fast, broad spread of information. What does that have to do R, or the R-bloggers community? First and foremost, we use the R-bloggers community as an example of the role of interest networks (see description below) in driving viral events. But we also used R as our go-to tool for our research that went into the book. Even the cover art, pictured here, was created with R, using the iGraph package. Included below is an excerpt from chapter 4 that includes the section on interest networks and R-bloggers.
R-bloggers.com is now three years young. The site is an (unofficial) online journal of the R statistical programming environment, written by bloggers who agreed to contribute their R articles to the site.
Last year, I posted on the top 24 R posts of 2011. In this post I wish to celebrate R-bloggers’ third birthmounth by sharing with you:
Links to the top 100 most read R posts of 2012
Statistics on “how well” R-bloggers did this year
My wishlist for the R community for 2013 (blogging about R, guest posts, and sponsors)
1. Top 100 R posts of 2012
R-bloggers’ success is thanks to the content submitted by the over 400 R bloggers who have joined r-bloggers. The R community currently has around 245 active R bloggers (links to the blogs are clearly visible in the right navigation bar on the R-bloggers homepage). In the past year, these bloggers wrote around 3200 posts about R!
Here is a list of the top visited posts on the site in 2012 (you can see the number of unique visitors in parentheses, while the list is ordered by the number of total page views):
This post shows how to print a prettier nested pivot table, created using the {reshape} package (similar to what you would get with Microsoft Excel), so you could print it either in the R terminal or as a LaTeX table. This task is done by bridging between the cast_df object produced by the {reshape} package, […]
This post shows how to print a prettier nested pivot table, created using the {reshape} package (similar to what you would get with Microsoft Excel), so you could print it either in the R terminal or as a LaTeX table. This task is done by bridging between the cast_df object produced by the {reshape} package, and the tabular function introduced by the new {tables} package.
Here is an example of the type of output we wish to produce in the R terminal:
The cast function is wonderful but it has one problem – the format of the output. As opposed to a pivot table in (for example) MS excel, the output of a nested table created by cast is very “flat”. That is, there is only one row for the header, and only one column for the row names. So for both the R terminal, or an Sweave document, when we deal with a more complex reshaping/aggregating, the result is not something you would be proud to send to a journal.
The opportunity: the {tables} package
The good news is that Duncan Murdoch have recently released a new package to CRAN called {tables}. The {tables} package can compute and display complex tables of summary statistics and turn them into nice looking tables in Sweave (LaTeX) documents. For using the full power of this package, you are invited to read through its detailed (and well written) 23 pages Vignette. However, some of us might have preferred to keep using the syntax of the {reshape} package, while also benefiting from the great formatting that is offered by the new {tables} package. For this purpose, I devised a function that bridges between cast_df (from {reshape}) and the tabular function (from {tables}).
The bridge: between the {tables} and the {reshape} packages
The code for the function is available on my github (link: tabular.cast_df.r on github) and it seems to works fine as far as I can see (though I wouldn’t run it on larger data files since it relies on melting a cast_df object.)
Here is an example for how to load and use the function:
####################### Loading the functions####################### Making sure we can source code from githubsource("https://www.r-statistics.com/wp-content/uploads/2012/01/source_https.r.txt")# Reading in the function for using tabular on a cast_df object:
source_https("https://raw.github.com/talgalili/R-code-snippets/master/tabular.cast_df.r")####################### example:################################### Loading and preparing some datarequire(reshape)names(airquality)<-tolower(names(airquality))
airquality2 <-airquality
airquality2$temp2 <-ifelse(airquality2$temp >median(airquality2$temp), "hot", "cold")
aqm <- melt(airquality2, id=c("month", "day","temp2"), na.rm=TRUE)colnames(aqm)[4]<-"variable2"# because otherwise the function is having problem when relying on the melt function of the cast objecthead(aqm,3)# month day temp2 variable2 value#1 5 1 cold ozone 41#2 5 2 cold ozone 36#3 5 3 cold ozone 12############# Running the example:
tabular.cast_df(cast(aqm, month ~ variable2, c(mean,sd)))
tabular(cast(aqm, month ~ variable2, c(mean,sd)))# notice how we turned tabular to be an S3 method that can deal with a cast_df object
Hmisc::latex(tabular(cast(aqm, month ~ variable2, c(mean,sd))))# this is what we would have used for an Sweave document
And here are the results in the terminal:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>> tabular.cast_df(cast(aqm, month ~ variable2, c(mean,sd)))
ozone solar.r wind temp
month meansdmeansdmeansdmeansd523.6222.22181.3115.0811.6233.53165.556.855629.4418.21190.292.8810.2673.76979.106.599759.1231.64216.580.578.9423.03683.904.316859.9639.68171.976.838.7943.22683.976.585931.4524.14167.479.1210.1803.46176.908.356> tabular(cast(aqm, month ~ variable2, c(mean,sd)))# notice how we turned tabular to be an S3 method that can deal with a cast_df object
ozone solar.r wind temp
month meansdmeansdmeansdmeansd523.6222.22181.3115.0811.6233.53165.556.855629.4418.21190.292.8810.2673.76979.106.599759.1231.64216.580.578.9423.03683.904.316859.9639.68171.976.838.7943.22683.976.585931.4524.14167.479.1210.1803.46176.908.356
And in an Sweave document:
Here is an example for the Rnw file that produces the above table: cast_df to tabular.Rnw
I will finish with saying that the tabular function offers more flexibility then the one offered by the function I provided. If you find any bugs or have suggestions of improvement, you are invited to leave a comment here or inside the code on github.
(Link-tip goes to Tony Breyal for putting together a solution for sourcing r code from github.)
The followings introductory post is intended for new users of R. It deals with interactive visualization using R through the iplots package.
This is a guest article by Dr. Robert I. Kabacoff, the founder of (one of) the first online R tutorials websites: Quick-R. Kabacoff has recently published the book ”R in Action“, providing a detailed walk-through for the R language based on various examples for illustrating R’s features (data manipulation, statistical methods, graphics, and so on…). In previous guest posts by Kabacoff we introduced data.frame objects in R and dealt with the Aggregation and Restructuring of data (using base R functions and the reshape package).
For readers of this blog, there is a 38% discount off the “R in Action” book (as well as all other eBooks, pBooks and MEAPs at Manning publishing house), simply by using the code rblogg38 when reaching checkout.
Let us now talk about Interactive Graphics with the iplots Package:
Interactive Graphics with the iplots Package
The base installation of R provides limited interactivity with graphs. You can modify graphs by issuing additional program statements, but there’s little that you can do to modify them or gather new information from them using the mouse. However, there are contributed packages that greatly enhance your ability to interact with the graphs you create—playwith, latticist, iplots, and rggobi. In this article, we’ll focus on functions provided by the iplots package. Be sure to install it before first use.
While playwith and latticist allow you to interact with a single graph, the iplots package takes interaction in a different direction. This package provides interactive mosaic plots, bar plots, box plots, parallel plots, scatter plots, and histograms that can be linked together and color brushed. This means that you can select and identify observations using the mouse, and highlighting observations in one graph will automatically highlight the same observations in all other open graphs. You can also use the mouse to obtain information about graphic objects such as points, bars, lines, and box plots.
The iplots package is implemented through Java and the primary functions are listed in table 1.
Table 1 iplot functions
Function
Description
ibar()
Interactive bar chart
ibox()
Interactive box plot
ihist()
Interactive histogram
imap()
Interactive map
imosaic()
Interactive mosaic plot
ipcp()
Interactive parallel coordinates plot
iplot()
Interactive scatter plot
To understand how iplots works, execute the code provided in listing 1.
Six windows containing graphs will open. Rearrange them on the desktop so that each is visible (each can be resized if necessary). A portion of the display is provided in figure 1.
Figure 1 An iplots demonstration created by listing 1. Only four of the six windows are displayed to save room. In these graphs, the user has clicked on the three-gear bar in the bar chart window.
Now try the following:
Click on the three-gear bar in the Barchart (gears) window. The bar will turn red. In addition, all cars with three-gear engines will be highlighted in the other graph windows.
Mouse down and drag to select a rectangular region of points in the Scatter plot (wt vs mpg) window. These points will be highlighted and the corresponding observations in every other graph window will also turn red.
Hold down the Ctrl key and move the mouse pointer over a point, bar, box plot, or line in one of the graphs. Details about that object will appear in a pop-up window.
Right-click on any object and note the options that are offered in the context menu. For example, you can right-click on the Boxplot (mpg) window and change the graph to a parallel coordinates plot (PCP).
You can drag to select more than one object (point, bar, and so on) or use Shift-click to select noncontiguous objects. Try selecting both the three- and five-gear bars in the Barchart (gears) window.
The functions in the iplots package allow you to explore the variable distributions and relationships among variables in subgroups of observations that you select interactively. This can provide insights that would be difficult and time-consuming to obtain in other ways. For more information on the iplots package, visit the project website at http://rosuda.org/iplots/.
Summary
In this article, we explored one of the several packages for dynamically interacting with graphs, iplots. This package allows you to interact directly with data in graphs, leading to a greater intimacy with your data and expanded opportunities for developing insights.
This article first appeared as chapter 16.4.4 from the “R in action“ book, and is published with permission from Manning publishing house. Other books in this serious which you might be interested in are (see the beginning of this post for a discount code):
Merging two data.frame objects in R is very easily done by using the merge function. While being very powerful, the merge function does not (as of yet) offer to return a merged data.frame that preserved the original order of, one of the two merged, data.frame objects. In this post I describe this problem, and offer […]
Update (2017-02-03) the dplyr package offers a great solution for this issue, see the document Two-table verbs for more details.
Merging two data.frame objects in R is very easily done by using the merge function. While being very powerful, the merge function does not (as of yet) offer to return a merged data.frame that preserved the original order of, one of the two merged, data.frame objects. In this post I describe this problem, and offer some easy to use code to solve it.
Let us start with a simple example:
x <-data.frame(
ref =c('Ref1', 'Ref2')
, label =c('Label01', 'Label02'))
y <-data.frame(
id =c('A1', 'C2', 'B3', 'D4')
, ref =c('Ref1', 'Ref2' , 'Ref3','Ref1')
, val =c(1.11, 2.22, 3.33, 4.44))######################## having a look at the two data.frame objects:> x
ref label
1 Ref1 Label01
2 Ref2 Label02
> y
id ref val
1 A1 Ref1 1.112 C2 Ref2 2.223 B3 Ref3 3.334 D4 Ref1 4.44
If we will now merge the two objects, we will find that the order of the rows is different then the original order of the “y” object. This is true whether we use “sort =T” or “sort=F”. You can notice that the original order was an ascending order of the “val” variable:
>merge( x, y, by='ref', all.y=T, sort=T)
ref label id val
1 Ref1 Label01 A1 1.112 Ref1 Label01 D4 4.443 Ref2 Label02 C2 2.224 Ref3 <NA> B3 3.33>merge( x, y, by='ref', all.y=T, sort=F)
ref label id val
1 Ref1 Label01 A1 1.112 Ref1 Label01 D4 4.443 Ref2 Label02 C2 2.224 Ref3 <NA> B3 3.33
This is explained in the help page of ?merge:
The rows are by default lexicographically sorted on the common columns, but for ‘sort = FALSE’ are in an unspecified order.
Or put differently: sort=FALSE doesn’t preserve the order of any of the two entered data.frame objects (x or y); instead it gives us an unspecified (potentially random) order.
However, it can so happen that we want to make sure the order of the resulting merged data.frame objects ARE ordered according to the order of one of the two original objects. In order to make sure of that, we could add an extra “id” (row index number) sequence on the dataframe we wish to sort on. Then, we can merge the two data.frame objects, sort by the sequence, and delete the sequence. (this was previously mentioned on the R-help mailing list by Bart Joosen).
Following is a function that implements this logic, followed by an example for its use:
############## function:
merge.with.order<-function(x,y, ..., sort=T, keep_order){# this function works just like merge, only that it adds the option to return the merged data.frame ordered by x (1) or by y (2)
add.id.column.to.data<-function(DATA){data.frame(DATA, id... =seq_len(nrow(DATA)))}# add.id.column.to.data(data.frame(x = rnorm(5), x2 = rnorm(5)))
order.by.id...and.remove.it<-function(DATA){# gets in a data.frame with the "id..." column. Orders by it and returns itif(!any(colnames(DATA)=="id..."))stop("The function order.by.id...and.remove.it only works with data.frame objects which includes the 'id...' order column")
ss_r <-order(DATA$id...)
ss_c <-colnames(DATA)!="id..."
DATA[ss_r, ss_c]}# tmp <- function(x) x==1; 1 # why we must check what to do if it is missing or not...# tmp()if(!missing(keep_order)){if(keep_order ==1)return(order.by.id...and.remove.it(merge(x=add.id.column.to.data(x),y=y,..., sort= FALSE)))if(keep_order ==2)return(order.by.id...and.remove.it(merge(x=x,y=add.id.column.to.data(y),..., sort= FALSE)))# if you didn't get "return" by now - issue a warning.warning("The function merge.with.order only accepts NULL/1/2 values for the keep_order variable")}else{return(merge(x=x,y=y,..., sort=sort))}}######### example:>merge( x.labels, x.vals, by='ref', all.y=T, sort=F)
ref label id val
1 Ref1 Label01 A1 1.112 Ref1 Label01 D4 4.443 Ref2 Label02 C2 2.224 Ref3 <NA> B3 3.33> merge.with.order( x.labels, x.vals, by='ref', all.y=T, sort=F ,keep_order =1)
ref label id val
1 Ref1 Label01 A1 1.112 Ref1 Label01 D4 4.443 Ref2 Label02 C2 2.224 Ref3 <NA> B3 3.33> merge.with.order( x.labels, x.vals, by='ref', all.y=T, sort=F ,keep_order =2)# yay - works as we wanted it to...
ref label id val
1 Ref1 Label01 A1 1.113 Ref2 Label02 C2 2.224 Ref3 <NA> B3 3.332 Ref1 Label01 D4 4.44
Here is a description for how to use the keep_order parameter:
keep_order can accept the numbers 1 or 2, in which case it will make sure the resulting merged data.frame will be ordered according to the original order of rows of the data.frame entered to x (if keep_order=1) or to y (if keep_order=2). If keep_order is missing, merge will continue working as usual. If keep_order gets some input other then 1 or 2, it will issue a warning that it doesn’t accept these values, but will continue working as merge normally would. Notice that the parameter “sort” is practically overridden when using keep_order (with the value 1 or 2).
The same code can be used to modify the original merge.data.frame function in base R, so to allow the use of the keep_order, here is a link to the patched merge.data.frame function (on github). If you can think of any ways to improve the function (or happen to notice a bug) please let me know either on github or in the comments. (also saying that you found the function to be useful will be fun to know about )
Update: Thanks to KY’s comment, I noticed the ?join function in the {plyr} library. This function is similar to merge (with less features, yet faster), and also automatically keeps the order of the x (first) data.frame used for merging, as explained in the ?join help page:
Unlike merge, (join) preserves the order of x no matter what join type is used. If needed, rows from y will be added to the bottom. Join is often faster than merge, although it is somewhat less featureful – it currently offers no way to rename output or merge on different variables in the x and y data frames.
The followings introductory post is intended for new users of R. It deals with the restructuring of data: what it is and how to perform it using base R functions and the {reshape} package. This is a guest article by Dr. Robert I. Kabacoff, the founder of (one of) the first online R tutorials websites: Quick-R. Kabacoff […]
The followings introductory post is intended for new users of R. It deals with the restructuring of data: what it is and how to perform it using base R functions and the {reshape} package.
This is a guest article by Dr. Robert I. Kabacoff, the founder of (one of) the first online R tutorials websites: Quick-R. Kabacoff has recently published the book ”R in Action“, providing a detailed walk-through for the R language based on various examples for illustrating R’s features (data manipulation, statistical methods, graphics, and so on…). The previous guest post by Kabacoff introduced data.frame objects in R.
For readers of this blog, there is a 38% discount off the “R in Action” book (as well as all other eBooks, pBooks and MEAPs at Manning publishing house), simply by using the code rblogg38 when reaching checkout.
Let us now talk about the Aggregation and Restructuring of data in R:
Aggregation and Restructuring
R provides a number of powerful methods for aggregating and reshaping data. When you aggregate data, you replace groups of observations with summary statistics based on those observations. When you reshape data, you alter the structure (rows and columns) determining how the data is organized. This article describes a variety of methods for accomplishing these tasks.
We’ll use the mtcars data frame that’s included with the base installation of R. This dataset, extracted from Motor Trend magazine (1974), describes the design and performance characteristics (number of cylinders, displacement, horsepower, mpg, and so on) for 34 automobiles. To learn more about the dataset, see help(mtcars).
Transpose
The transpose (reversing rows and columns) is perhaps the simplest method of reshaping a dataset. Use the t() function to transpose a matrix or a data frame. In the latter case, row names become variable (column) names. An example is presented in the next listing.
Listing 1 uses a subset of the mtcars dataset in order to conserve space on the page. You’ll see a more flexible way of transposing data when we look at the reshape package later in this article.
Aggregating data
It’s relatively easy to collapse data in R using one or more by variables and a defined function. The format is
1
aggregate(x, by, FUN)
where x is the data object to be collapsed, by is a list of variables that will be crossed to form the new observations, and FUN is the scalar function used to calculate summary statistics that will make up the new observation values.
As an example, we’ll aggregate the mtcars data by number of cylinders and gears, returning means on each of the numeric variables (see the next listing).
Listing 2 Aggregating data
1
2
3
4
5
6
7
8
9
10
11
12
13
>options(digits=3)>attach(mtcars)> aggdata <-aggregate(mtcars, by=list(cyl,gear), FUN=mean, na.rm=TRUE)> aggdata
Group.1 Group.2 mpg cyl disp hp drat wt qsec vs am gear carb
14321.54120973.702.4620.01.00.0031.0026319.862421082.923.3419.81.00.0031.0038315.183581943.124.1017.10.00.0033.0844426.94103764.112.3819.61.00.7541.5056419.861641163.913.0917.70.50.5044.0064528.241081024.101.8316.80.51.0052.0076519.761451753.622.7715.50.01.0056.0088515.483263003.883.3714.60.01.0056.00
In these results, Group.1 represents the number of cylinders (4, 6, or and Group.2 represents the number of gears (3, 4, or 5). For example, cars with 4 cylinders and 3 gears have a mean of 21.5 miles per gallon (mpg).
When you’re using the aggregate() function , the by variables must be in a list (even if there’s only one). You can declare a custom name for the groups from within the list, for instance, using by=list(Group.cyl=cyl, Group.gears=gear).
The function specified can be any built-in or user-provided function. This gives the aggregate command a great deal of power. But when it comes to power, nothing beats the reshape package.
The reshape package
The reshape package is a tremendously versatile approach to both restructuring and aggregating datasets. Because of this versatility, it can be a bit challenging to learn.
We’ll go through the process slowly and use a small dataset so that it’s clear what’s happening. Because reshape isn’t included in the standard installation of R, you’ll need to install it one time, using install.packages(“reshape”).
Basically, you’ll “melt” data so that each row is a unique ID-variable combination. Then you’ll “cast” the melted data into any shape you desire. During the cast, you can aggregate the data with any function you wish. The dataset you’ll be working with is shown in table 1.
Table 1 The original dataset (mydata)
ID
Time
X1
X2
1
1
5
6
1
2
3
5
2
1
6
1
2
2
2
4
In this dataset, the measurements are the values in the last two columns (5, 6, 3, 5, 6, 1, 2, and 4). Each measurement is uniquely identified by a combination of ID variables (in this case ID, Time, and whether the measurement is on X1 or X2). For example, the measured value 5 in the first row is uniquely identified by knowing that it’s from observation (ID) 1, at Time 1, and on variable X1.
Melting
When you melt a dataset, you restructure it into a format where each measured variable is in its own row, along with the ID variables needed to uniquely identify it. If you melt the data from table 1, using the following code
Note that you must specify the variables needed to uniquely identify each measurement (ID and Time) and that the variable indicating the measurement variable names (X1 or X2) is created for you automatically.
Now that you have your data in a melted form, you can recast it into any shape, using the cast() function.
Casting
The cast() function starts with melted data and reshapes it using a formula that you provide and an (optional) function used to aggregate the data. The format is
1
newdata <- cast(md, formula, FUN)
Where md is the melted data, formula describes the desired end result, and FUN is the (optional) aggregating function. The formula takes the form
1
rowvar1 + rowvar2 + … ~ colvar1 + colvar2 + …
In this formula, rowvar1 + rowvar2 + … define the set of crossed variables that define the rows, and colvar1 + colvar2 + … define the set of crossed variables that define the columns. See the examples in figure 1. (click to enlarge the image)
Figure 1 Reshaping data with the melt() and cast() functions
Because the formulas on the right side (d, e, and f) don’t include a function, the data is reshaped. In contrast, the examples on the left side (a, b, and c) specify the mean as an aggregating function. Thus the data are not only reshaped but aggregated as well. For example, (a) gives the means on X1 and X2 averaged over time for each observation. Example (b) gives the mean scores of X1 and X2 at Time 1 and Time 2, averaged over observations. In (c) you have the mean score for each observation at Time 1 and Time 2, averaged over X1 and X2.
As you can see, the flexibility provided by the melt() and cast() functions is amazing. There are many times when you’ll have to reshape or aggregate your data prior to analysis. For example, you’ll typically need to place your data in what’s called long format resembling table 2 when analyzing repeated measures data (data where multiple measures are recorded for each observation).
Summary
Chapter 5 ofR in Action reviews many of the dozens of mathematical, statistical, and probability functions that are useful for manipulating data. In this article, we have briefly explored several ways of aggregating and restructuring data.
This article first appeared as chapter 5.6 from the “R in action“ book, and is published with permission from Manning publishing house. Other books in this serious which you might be interested in are (see the beginning of this post for a discount code):
R-bloggers.com is now two years young. The site is an (unofficial) online R journal written by bloggers who agreed to contribute their R articles to the site. In this post I wish to celebrate R-bloggers’ second birthmounth by sharing with you: Links to the top 20 posts of 2011 Statistics on “how well” R-bloggers did […]
R-bloggers.com is now two years young. The site is an (unofficial) online R journal written by bloggers who agreed to contribute their R articles to the site. In this post I wish to celebrate R-bloggers’ second birthmounth by sharing with you:
Links to the top 20 posts of 2011
Statistics on “how well” R-bloggers did this year
An invitation for sponsors/supporters to help keep the site alive
1. Top 24 R posts of 2011
R-bloggers’ success is largely owed to the content submitted by the R bloggers themselves. The R community currently has almost 300 active R bloggers (links to the blogs are clearly visible in the right navigation bar on the R-bloggers homepage). In the past year, these bloggers wrote over 2800 posts about R.
Here is a list of the top visited posts on the site in 2011:
2. Statistics – how well did R-bloggers do this year
There are several matrices one can consider when evaluating the success of a website. I’ll present a few of them here and will begin by talking about the visitors to the site.
This year, the site was visited by over 665,000 “Unique Visitors.” There was a total of over 1.4 million visits and over 2.8 million page-views. People have surfed the site from over 200 countries, with the greatest number of visitors coming from the United States (~40%) and then followed by the United Kingdom (6.9%), Germany (6.6%), Canada (4.7%), France (3.3%), and other countries.
The site has received between 15,000 to 45,000 visits a week in the past few months, and I suspect this number will remain stable in the next few months (unless something very interesting will happen).
I believe this number will stay constant thanks to visitors’ loyalty: 55% of the site’s visits came from returning users.
Another indicator of reader loyalty is the number of subscribers to R-bloggers as counted by feedburner, which includes both RSS readers and e-mail subscribers. The range of subscribers is estimated to be between 5600 to 5900.
Thus, I am very happy to see that R-bloggers continues to succeed in offering a real service to the global R users community.
3. Invitation to sponsor/advertise on R-bloggers
This year I was sadly accused by google adsense of click fraud (which I did not do, but have no way of proving my innocence). Therefor, I am no longer able to use google adsense to sustain R-bloggers high monthly bills, and I turned to rely on direct sponsoring of R-bloggers.
If you are interested in sponsoring/placing-ads/supporting R-bloggers, then you are welcome to contact me.
The followings introductory post is intended for new users of R. It deals with R data frames: what they are, and how to create, view, and update them. This is a guest article by Dr. Robert I. Kabacoff, the founder of (one of) the first online R tutorials websites: Quick-R. Kabacoff has recently published the book ”R […]
The followings introductory post is intended for new users of R. It deals with R data frames: what they are, and how to create, view, and update them.
This is a guest article by Dr. Robert I. Kabacoff, the founder of (one of) the first online R tutorials websites: Quick-R. Kabacoff has recently published the book ”R in Action“, providing a detailed walk-through for the R language based on various examples for illustrating R’s features (data manipulation, statistical methods, graphics, and so on…)
For readers of this blog, there is a 38% discount off the “R in Action” book (as well as all other eBooks, pBooks and MEAPs at Manning publishing house), simply by using the code rblogg38 when reaching checkout.
Let us now talk about data frames:
Data Frames
A data frame is more general than a matrix in that different columns can contain different modes of data (numeric, character, and so on). It’s similar to the datasets you’d typically see in SAS, SPSS, and Stata. Data frames are the most common data structure you’ll deal with in R.
The patient dataset in table 1 consists of numeric and character data.
Table 1: A patient dataset
PatientID
AdmDate
Age
Diabetes
Status
1
10/15/2009
25
Type1
Poor
2
11/01/2009
34
Type2
Improved
3
10/21/2009
28
Type1
Excellent
4
10/28/2009
52
Type1
Poor
Because there are multiple modes of data, you can’t contain this data in a matrix. In this case, a data frame would be the structure of choice.
A data frame is created with the data.frame() function:
1
mydata <-data.frame(col1, col2, col3,…)
where col1, col2, col3, … are column vectors of any type (such as character, numeric, or logical). Names for each column can be provided with the names function.
Each column must have only one mode, but you can put columns of different modes together to form the data frame. Because data frames are close to what analysts typically think of as datasets, we’ll use the terms columns and variables interchangeably when discussing data frames.
There are several ways to identify the elements of a data frame. You can use the subscript notation or you can specify column names. Using the patientdata data frame created earlier, the following listing demonstrates these approaches.
Listing 2 Specifying elements of a data frame
1
2
3
4
5
6
7
8
9
10
11
12
13
14
> patientdata[1:2]
patientID age
1125223433284452> patientdata[c("diabetes", "status")]
diabetes status
1 Type1 Poor
2 Type2 Improved
3 Type1 Excellent
4 Type1 Poor
> patientdata$age #age variable in the patient data frame[1]25342852
The $ notation in the third example is used to indicate a particular variable from a given data frame. For example, if you want to cross-tabulate diabetes type by status, you could use the following code:
It can get tiresome typing patientdata$ at the beginning of every variable name, so shortcuts are available. You can use either the attach() and detach() or with() functions to simplify your code.
attach, detach, and with
The attach() function adds the data frame to the R search path. When a variable name is encountered, data frames in the search path are checked in order to locate the variable. Using a sample (mtcars) data frame, you could use the following code to obtain summary statistics for automobile mileage (mpg), and plot this variable against engine displacement (disp), and weight (wt):
The detach() function removes the data frame from the search path. Note that detach() does nothing to the data frame itself. The statement is optional but is good programming practice and should be included routinely.
The limitations with this approach are evident when more than one object can have the same name. Consider the following code:
1
2
3
4
5
6
7
8
9
> mpg <-c(25, 36, 47)>attach(mtcars)
The following object(s) are masked _by_ ‘.GlobalEnv’: mpg
>plot(mpg, wt)
Error inxy.coords(x, y, xlabel, ylabel, log):
‘x’ and ‘y’ lengths differ
> mpg
[1]253647
Here we already have an object named mpg in our environment when the mtcars data frame is attached. In such cases, the original object takes precedence, which isn’t what you want. The plot statement fails because mpg has 3 elements and disp has 32 elements. The attach() and detach() functions are best used when you’re analyzing a single data frame and you’re unlikely to have multiple objects with the same name. In any case, be vigilant for warnings that say that objects are being masked.
An alternative approach is to use the with() function. You could write the previous example as
In this case, the statements within the {} brackets are evaluated with reference to the mtcars data frame. You don’t have to worry about name conflicts here. If there’s only one statement (for example, summary(mpg)), the {} brackets are optional.
The limitation of the with() function is that assignments will only exist within the function brackets. Consider the following:
1
2
3
4
5
6
7
8
>with(mtcars, {
stats <-summary(mpg)
stats
})
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.4015.4319.2020.0922.8033.90> stats
Error: object ‘stats’ not found
If you need to create objects that will exist outside of the with() construct, use the special assignment operator <<- instead of the standard one (<-). It will save the object to the global environment outside of the with() call. This can be demonstrated with the following code:
1
2
3
4
5
6
7
8
9
>with(mtcars, {
nokeepstats <-summary(mpg)
keepstats <<-summary(mpg)})> nokeepstats
Error: object ‘nokeepstats’ not found
> keepstats
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.4015.4319.2020.0922.8033.90
Most books on R recommend using with() over attach(). I think that ultimately the choice is a matter of preference and should be based on what you’re trying to achieve and your understanding of the implications.
Case identifiers
In the patient data example, patientID is used to identify individuals in the dataset. In R, case identifiers can be specified with a rowname option in the data frame function. For example, the statement
specifies patientID as the variable to use in labeling cases on various printouts and graphs produced by R.
Summary
One of the most challenging tasks in data analysis is data preparation. R provides various structures for holding data and many methods for importing data from both keyboard and external sources. One of those structures is data frames, which we covered here. Your ability to specify elements of these structures via the bracket notation is particularly important in selecting, subsetting, and transforming data.
R offers a wealth of functions for accessing external data. This includes data from flat files, web files, statistical packages, spreadsheets, and databases. Note that you can also export data from R into these external formats. We showed you how to use either the attach() and detach() or with() functions to simplify your code.
Links to slides and talks from useR 2011 – all organized in one page.
I was recently reminded that the wonderful team at warwick University made sure to put online many of the slides (and some videos) of talks from the recent useR 2011 conference. You can browse through the talks by going between the timetables (where it will be the most updated, if more slides will be added later), but I thought it might be more convenient for some of you to have the links to all the talks (with slides/videos) in one place.
I am grateful for all of the wonderful people who put their time in making such an amazing event (organizers, speakers, attendees), and also for the many speakers who made sure to share their talk/slides online for all of us to reference. I hope to see this open-slides trend will continue in the upcoming useR conferences…
A Bernoulli process is a sequence of Bernoulli trials (the realization of n binary random variables), taking two values (0/1, Heads/Tails, Boy/Girl, etc…). It is often used in teaching introductory probability/statistics classes about the binomial distribution. When visualizing a Bernoulli process, it is common to use a binary tree diagram in order to show the […]
A Bernoulli process is a sequence of Bernoulli trials (the realization of n binary random variables), taking two values (0/1, Heads/Tails, Boy/Girl, etc…). It is often used in teaching introductory probability/statistics classes about the binomial distribution.
When visualizing a Bernoulli process, it is common to use a binary tree diagram in order to show the progression of the process, as well as the various consequences of the trial. We might also include the number of “successes”, and the probability for reaching a specific terminal node.
I wanted to be able to create such a diagram using R. For this purpose I composed some code which uses the {diagram} R package. The final function should allow one to create different sizes of diagrams, while allowing flexibility with regards to the text which is used in the tree.
Here is an example of the simplest use of the function:
source("https://www.r-statistics.com/wp-content/uploads/2011/11/binary.tree_.for_.binomial.game_.r.txt") # loading the function
binary.tree.for.binomial.game(2) # creating a tree for B(2,0.5)
The resulting diagram will look like this:
The same can be done for creating larger trees. For example, here is the code for a 4 stage Bernoulli process:
source("https://www.r-statistics.com/wp-content/uploads/2011/11/binary.tree_.for_.binomial.game_.r.txt") # loading the function
binary.tree.for.binomial.game(4) # creating a tree for B(4,0.5)
The resulting diagram will look like this:
The function can also be tweaked in order to describe a more specific story. For example, the following code describes a 3 stage Bernoulli process where an unfair coin is tossed 3 times (with probability of it giving heads being 0.8):