Chapter 3: Exploring Data

In this chapter, we will cover the basics of how one should use summary statistics to explore data, use simple data visualization to find problems and discuss how to address those problems.

Any data set typically contains a lot of missing values or even worse, incorrect data, due to various reasons. The first step always should be to get to know your data by exploring each variable.

For this chapter, let’s use data set affairs from package wooldridge based on Dr. Fairs research on affairs with data from a survey by Psychology Today.

Econometric analysis usually begins with us observing some x and y variables that represent some population and we are interested in “explaining variable y in terms of x” or “studying how y varies with changes in x”. However, before we write anything down, we need to think about these three questions: (1) how do we allow for other factors (other than x) to affect y; (2) what is the functional form of the relationship between x and y; (3) how are we sure that ceteris paribus assumption is captured when estimating relationship between x and y?

data(affairs, package='wooldridge')

Full list of variables in affairs data set:

Number Name of the variable Description
1. id identifier
2. male =1 if male
3. age in years
4. yrsmarr years married
5. kids =1 if have kids
6. relig 5 = very relig., 4 = somewhat, 3 = slightly, 2 = not at all, 1 = anti
7. educ years schooling
8. occup occupation, reverse Hollingshead scale
9. ratemarr 5 = vry hap marr, 4 = hap than avg, 3 = avg, 2 = smewht unhap, 1 = vry unhap
10. naffairs number of affairs within last year
11. affair =1 if had at least one affair
12. vryhap ratemarr == 5
13. hapavg ratemarr == 4
14. avgmarr ratemarr == 3
15. unhap ratemarr == 2
16. vryrel relig == 5
17. smerel relig == 4
18. slghtrel relig == 3
19. notrel relig == 2

Typical problems that can be easily identified when working with data sets are: - incorrectly programmed variables - missing values - invalid values and outliers - data ranges that are too wide or too narrow - units of data do not match

What I mean by incorrectly programmed variables? While technically, it is not always a problem, in R and other statistical software, variables can be binary (1 or 0), ordered (1<2<3), integer, numeric and so on. In many data sets, factor variables are often defined as integer. For example, see ratemarr variable in which people rate their marriages from “very happy marriage” to “very unhappy”.

class(affairs$ratemarr)
## [1] "integer"

To redefine as what it actually is - an ordered factor - we can use the following commands: affairs\(ratemarr=ordered(affairs\)ratemarr, levels=c(1,2,3,4,5)) levels(affairs$ratemarr) = c(“Very unhappy”, “Somewhat unhappy”, “Average”, “Happy”, “Very happy”)

head(affairs$ratemarr)
## [1] 4 4 4 4 5 5
plot(affairs$ratemarr)

Also, let’s make the male gender dummy as a dummy variable.

affairs$male=factor(affairs$male, levels=c(0,1))
levels(affairs$male) = c("Female", "Male")
head(affairs$male)
## [1] Male   Female Male   Female Female Male  
## Levels: Female Male
plot(affairs$male)

Similarly, sometimes strings of words in R are read as a bunch of characters, instead of ordered factors. Create this variable and see if it recognizes it as a factor

affairs$randomvar = c(rep("A",300), rep("B",301))
class( affairs$randomvar) #not good
## [1] "character"
affairs$randomvar = factor( affairs$randomvar)
class( affairs$randomvar)
## [1] "factor"

If a data set contains only a few missing observations, it may not be a problem. However, if there are a large number of missing values for some variables, we are not able to use them for data analysis without additional work with those variables. Various software as well as commands in R will quietly drop observation rows with missing values. You need to be careful when conducting analysis and follow among other things, the number of observations used in a particular computation.

The most convenient and easy to use command that shows you the basic statistics by variable is summary. Using the summary command, let’s examine the variables.

summary(affairs)
##        id           male          age           yrsmarr      
##  Min.   :   4   Female:315   Min.   :17.50   Min.   : 0.125  
##  1st Qu.: 528   Male  :286   1st Qu.:27.00   1st Qu.: 4.000  
##  Median :1009                Median :32.00   Median : 7.000  
##  Mean   :1060                Mean   :32.49   Mean   : 8.178  
##  3rd Qu.:1453                3rd Qu.:37.00   3rd Qu.:15.000  
##  Max.   :9029                Max.   :57.00   Max.   :15.000  
##       kids            relig            educ           occup      
##  Min.   :0.0000   Min.   :1.000   Min.   : 9.00   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:2.000   1st Qu.:14.00   1st Qu.:3.000  
##  Median :1.0000   Median :3.000   Median :16.00   Median :5.000  
##  Mean   :0.7155   Mean   :3.116   Mean   :16.17   Mean   :4.195  
##  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:18.00   3rd Qu.:6.000  
##  Max.   :1.0000   Max.   :5.000   Max.   :20.00   Max.   :7.000  
##     ratemarr        naffairs          affair           vryhap     
##  Min.   :1.000   Min.   : 0.000   Min.   :0.0000   Min.   :0.000  
##  1st Qu.:3.000   1st Qu.: 0.000   1st Qu.:0.0000   1st Qu.:0.000  
##  Median :4.000   Median : 0.000   Median :0.0000   Median :0.000  
##  Mean   :3.932   Mean   : 1.456   Mean   :0.2496   Mean   :0.386  
##  3rd Qu.:5.000   3rd Qu.: 0.000   3rd Qu.:0.0000   3rd Qu.:1.000  
##  Max.   :5.000   Max.   :12.000   Max.   :1.0000   Max.   :1.000  
##      hapavg          avgmarr           unhap            vryrel      
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.0000   Median :0.0000   Median :0.0000  
##  Mean   :0.3228   Mean   :0.1547   Mean   :0.1098   Mean   :0.1165  
##  3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:0.0000  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##      smerel          slghtrel          notrel       randomvar
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   A:300    
##  1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   B:301    
##  Median :0.0000   Median :0.0000   Median :0.0000            
##  Mean   :0.3161   Mean   :0.2146   Mean   :0.2729            
##  3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:1.0000            
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000

For example, looking at variable age, we see that the lowest recorded value is 17.5 and the highest is 57. One would be sure something is not right if age was negative, or below age of 10 when discussing affairs. Similarly, any age above 100 should also be examined. It may be technically possible that there are some 100-year-olds having affairs, it is not very likely. More variation in a variable allows for a more accurate analysis. Also, take a look at outliers, significantly differing value from the rest of the observations, that may have overly strong effect on the statistical analysis. Finally, when doing analysis make sure you keep track of the units of measurement well to avoid confusion and incorrect interpretation. There are many differences that come when income is measured as hourly wage or monthly in thousands of dollars while in the data it may look similar. For example, wage of 5 may mean 5 dollars per hour or 5 thousand dollars per month. Keep track of your units.

To check if we have any missing values, we can use a very simple command. Since it checks all of the points in the data set, and returns a large matrix of TRUES and FALSES, you may want to sum it all up. When you sum up a TRUE_FALSE binary values, TRUE is counted as 1, FALSE as 0.

head(is.na(affairs))
##      id  male   age yrsmarr  kids relig  educ occup ratemarr naffairs
## 1 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
## 2 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
## 3 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
## 4 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
## 5 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
## 6 FALSE FALSE FALSE   FALSE FALSE FALSE FALSE FALSE    FALSE    FALSE
##   affair vryhap hapavg avgmarr unhap vryrel smerel slghtrel notrel
## 1  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
## 2  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
## 3  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
## 4  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
## 5  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
## 6  FALSE  FALSE  FALSE   FALSE FALSE  FALSE  FALSE    FALSE  FALSE
##   randomvar
## 1     FALSE
## 2     FALSE
## 3     FALSE
## 4     FALSE
## 5     FALSE
## 6     FALSE
sum(is.na(affairs))
## [1] 0

As data summary statistics can give a lot of information, so does data visualization. We generally call use of graphics to examine data - visualization. We want these visuals or figures to convey as much information as possible without being too difficult to read and they must be clear, and the main results should stand out. We pose questions, graph the data, learn what we can, and regraph the data to answer subsequent questions that may arise from previous figures.

Some of the most popular data visualizations for one variable are histograms, density plots, bar charts and dot plots.

Histograms bins a variable into fixed-width buckets and returns the number of data points that falls into each bucket as a height. To create a histogram, we can use a simple command ‘hist’ or use a set of commands from package ‘ggplot2’. The issue with histograms is that deciding on the width of the bucket may determine what you learn from the figure. Wide bins are easy to read but lose some information while narrow bins may be hard to read.

hist(affairs$age)
#install.packages("ggplot2")
library(ggplot2)

ggplot(affairs, aes(x=age)) +
              geom_histogram(binwidth=5, fill="gray")

Density plots is like a continuous histogram with the area under the plot is rescaled to equal one. When we look at density plot, we are interested in the overall shape of the curve than the actual observed values.

plot(density(affairs$age))

ggplot(affairs, aes(x=age)) + geom_density()

In some cases, a transformation of a variable such as a natural log, or log10 can be useful in finding more about your data. You should use a logarithmic scale when percent change, or change in orders of magnitude, is more important than changes in absolute units. You should also use a log scale to better visualize data that is heavily skewed.

A is a histogram for discrete data: it records the frequency of every value of a bar chart categorical variable. For example we can look at the number of survey respondents by how they feel about their marriage from “very unhappy” to “very happy”. While it may be a nice-looking figure, it does not tell us any more than a simple summary statistic.

A=c(sum(affairs$ratemarr=="Very unhappy"),
  sum(affairs$ratemarr=="Somewhat unhappy"),
  sum(affairs$ratemarr=="Average"),
  sum(affairs$ratemarr=="Happy"),
  sum(affairs$ratemarr=="Very happy")  )
barplot(A)

ggplot(affairs, aes(x=ratemarr)) + geom_bar(fill="gray")

In some cases, especially when names of the variables are long, one can use a horizontal bar chart or a dot plot. Here, we take a look at the discrete counts of respondents by occupation. In the second graph, we graph a sorted dot plot.

ggplot(affairs, aes(x=occup)) +
   geom_bar(fill="gray") +
   coord_flip() +
  scale_x_continuous(breaks=seq(1,7,1))

#install.packages(WVPlots)
library(WVPlots)
ClevelandDotPlot(affairs, "occup",
   sort = 1, title="Occupation") +
   coord_flip()

In all the figures above, we looked at one variable at a time. We also typically look at how two variables are related. For that, we can use scatterplot, line plots, bar charts, variations of histograms and density plots.

First, let’s take a look at a scatterplot of number of affairs by age. Using ggplot2 package, we can also easily include a smoothing line which calculates smoothed local linear fits of the data. It allows us to better see what is happening in the data.

ggplot(affairs, aes(x=age, y=naffairs)) +
  geom_point() +
  ggtitle("Number of affairs as a function of age")

#alternatively, one could use a simple plot command:   plot(naffairs~age,data=affairs)

ggplot(affairs, aes(x=age, y=naffairs)) +
   geom_point() + geom_smooth() +
   ggtitle("Number of affairs as a function of age")

A smoothing curve is also useful in a scatterplot of a continuous and a binary variable. Otherwise, a scatterplot is not very useful. A hexibin plot is like a two-dimensional histogram. The data is divided into bins, and the number of data points in each bin is represented by color or shading. Darker bins = more counts. See figures showing relationships between “very happy” and age, and between “how religious one is” and age.

BinaryYScatterPlot(affairs, "age", "vryhap",
                   title = "Probability of being 'very happy' in marriage by age")

HexBinPlot(affairs, "age", "relig", "Religious-ness as a function of age") +
  geom_smooth(color="black", se=FALSE)

We can use bar chart not only for one single variable at a time. We can, for example, look at how people rate their marriage and if they had an affair. The red part of the bar chart indicates the counts of people who did not have an affair according to their rating of their marriage. Since most people rated their marriages as very happy and very few indicated that their marriages are “very unhappy”, we can normalize each category to 1. The last figure (plot 4) shows the fraction of people who had an affair by how they rate their marriage.

affairs$affair=factor(affairs$affair)
levels(affairs$affair)=c("No","Yes")

#Plot 1
ggplot(affairs, aes(x=ratemarr, fill=affair)) +
  geom_bar()

#Plot 2
ggplot(affairs, aes(x=ratemarr, fill=affair)) +
  geom_bar(position = "dodge")

#Plot 3
ShadowPlot(affairs, "ratemarr", "affair",
           title = "Affairs by Happines in Marriage")

#Plot 4
ggplot(affairs, aes(x=ratemarr, fill=affair)) +
  geom_bar(position = "fill")

Using a horizontal bar, we can examine the relationship between gender and how people rate their marriages. We see that in our dataset, men tend to rate their marriages more in the middle, women are slightly more likely to rate marriages at one of the extreme options.

ggplot(affairs, aes(x=ratemarr, fill=male)) +
  geom_bar(position = "dodge") +
  scale_fill_brewer(palette = "Dark2") +
  coord_flip()

ggplot(affairs, aes(x=ratemarr)) +
  geom_bar(fill="darkgray") +
  facet_wrap(~male, scale="free_x") +
  coord_flip()

We can also superimpose density plots. For example, let’s look at individuals who had and who did not have an affair by age. ggplot(affairs, aes(x=age, color=affair, linetype=affair)) + geom_density() + scale_color_brewer(palette=“Dark2”)

ShadowHist(affairs, "age", "affair",
           "Age distribution for people engaged in and not engaged in affairs",
           binwidth=5) 

Similarly, we can examine the age distributions of our data by a specific category. For example, lets look at the density plots by how people rate their marriage. We see that young people tend to rate their marriages overwhelmingly “happy” or “very happy”.

ggplot(affairs, aes(x=age)) +
  geom_density() + facet_wrap(~ratemarr)

References

Zumel, N., & Mount, J. (2014). Practical Data Science With R. Manning Publications Co.

Wooldridge, J. (2019). Introductory econometrics: a modern approach. Boston, MA: Cengage.