Monday, September 11, 2017

Julia - Language - DataFrames - Dealing with NA values

Another common problem in data entry is that of missing values. This results in data point values of the NA type. They create havoc trying to work with values in a Dataframe. Fortunately, we can get rid of rows that contain NA values in a few ways. 

$ julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-pc-linux-gnu

julia> using DataFrames

julia> numeric_df = DataFrame(Col1= 1:10, Col2 = 11:20, Col3 = 21:30)
10×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ 3    │ 13   │ 23   │
│ 4   │ 4    │ 14   │ 24   │
│ 5   │ 5    │ 15   │ 25   │
│ 6   │ 6    │ 16   │ 26   │
│ 7   │ 7    │ 17   │ 27   │
│ 8   │ 8    │ 18   │ 28   │
│ 9   │ 9    │ 19   │ 29   │
│ 10  │ 10   │ 20   │ 30   │

julia> 


Let us set some NA values:

julia> numeric_df[3,:Col1] = NA
NA

julia> numeric_df[4,:Col2] = NA
NA

julia> numeric_df[[5,9],:Col3] = NA  # row 5 and 9 in column C
NA

julia> numeric_df
10×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ NA   │ 13   │ 23   │
│ 4   │ 4    │ NA   │ 24   │
│ 5   │ 5    │ 15   │ NA   │
│ 6   │ 6    │ 16   │ 26   │
│ 7   │ 7    │ 17   │ 27   │
│ 8   │ 8    │ 18   │ 28   │
│ 9   │ 9    │ 19   │ NA   │
│ 10  │ 10   │ 20   │ 30   │

julia> 


The completecases() function returns Boolean values for each row, with a false return if the rows contain a NA value. 

julia> completecases(numeric_df)
10-element DataArrays.DataArray{Bool,1}:
  true
  true
 false
 false
 false
  true
  true
  true
 false
  true

julia> 

The completecases!() function permanentely deletes rows with NA values.

julia> completecases!(numeric_df)
6×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ 6    │ 16   │ 26   │
│ 4   │ 7    │ 17   │ 27   │
│ 5   │ 8    │ 18   │ 28   │
│ 6   │ 10   │ 20   │ 30   │

julia> numeric_df
6×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ 6    │ 16   │ 26   │
│ 4   │ 7    │ 17   │ 27   │
│ 5   │ 8    │ 18   │ 28   │
│ 6   │ 10   │ 20   │ 30   │

julia> 

We observe that the rows containing NA values are deleted. Let us now recreate the DataFrame numeric_df to use another way of deleting rows with NA values.

julia> numeric_df = DataFrame(Col1= 1:10, Col2 = 11:20, Col3 = 21:30);

julia> numeric_df
10×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ 3    │ 13   │ 23   │
│ 4   │ 4    │ 14   │ 24   │
│ 5   │ 5    │ 15   │ 25   │
│ 6   │ 6    │ 16   │ 26   │
│ 7   │ 7    │ 17   │ 27   │
│ 8   │ 8    │ 18   │ 28   │
│ 9   │ 9    │ 19   │ 29   │
│ 10  │ 10   │ 20   │ 30   │


Also, let us reset the NA values

julia> numeric_df[3,:Col1] = NA
NA

julia> numeric_df[4,:Col2] = NA
NA

julia> numeric_df[[5,9],:Col3] = NA  # row 5 and 9 in column C
NA

julia> 

Back to the original DataFrame, we can use isna() function to show whether a value is of NA type:

julia> isna(numeric_df[:Col1])
10-element BitArray{1}:
 false
 false
  true
 false
 false
 false
 false
 false
 false
 false

julia> 

By adding the findin() function we can identify only the NA rows. The findin() function allows us to specify what we want to find, i.e, true or false boolean values (in this case for the isna())

julia> findin(isna(numeric_df[:Col1]),true)
1-element Array{Int64,1}:
 3

julia> 

We can also use the find() function to simply find the rows with NA values. 

julia> find(isna(numeric_df[:Col1]))
1-element Array{Int64,1}:
 3

julia> 


This presents us with a way to delete all the rows that contain NA values. 

julia> rows,cols = size(numeric_df)
(10, 3)

julia> rows
10

julia> cols
3

julia> 


Creating a for loop to go through all the columns and deleting rows with NA values:

julia> for i in 1:cols
       deleterows!(numeric_df, find(isna(numeric_df[:,i])))
       end

julia> numeric_df
6×3 DataFrames.DataFrame
│ Row │ Col1 │ Col2 │ Col3 │
├─────┼──────┼──────┼──────┤
│ 1   │ 1    │ 11   │ 21   │
│ 2   │ 2    │ 12   │ 22   │
│ 3   │ 6    │ 16   │ 26   │
│ 4   │ 7    │ 17   │ 27   │
│ 5   │ 8    │ 18   │ 28   │
│ 6   │ 10   │ 20   │ 30   │

julia> 





Julia - Language - DataFrames - Handling Duplicate rows in DataFrames

Duplicate Rows:


Data Entry is the caputring of data points. It is almost never a clean process. The repeat entry of data is one common mistake that is made when data is captured. Removal of duplicate entries is using the unique() function. Selected rows can also be removed for any reason [not just because they are duplicate rows] using the deleterows!() function. Adding a bang (!) , that is unique!(), results in a function that makes the changes permanent. 

$ julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-pc-linux-gnu

julia> using DataFrames;

julia> my_movies_df = DataFrame(MovieName=["Amistad","Batman Begins","Catch Me If You Can","Dunkirk","Empire of the Sun","Firelight","Gladiator","Hannibal","Interstellar","Jaws","King Arthur","Lincoln","Memento","Amistad"],Director=["Steven Spielberg","Christopher Nolan","Steven Spielberg","Christopher Nolan","Steven Spielberg","Steven Spielberg","Ridley Scott","Ridley Scott","Christopher Nolan","Steven Spielberg","Antoine Fuqua","Steven Spielberg","Christopher Nolan","Steven Spielberg"]);

julia> my_movies_df
14×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Empire of the Sun"   │ "Steven Spielberg"  │
│ 6   │ "Firelight"           │ "Steven Spielberg"  │
│ 7   │ "Gladiator"           │ "Ridley Scott"      │
│ 8   │ "Hannibal"            │ "Ridley Scott"      │
│ 9   │ "Interstellar"        │ "Christopher Nolan" │
│ 10  │ "Jaws"                │ "Steven Spielberg"  │
│ 11  │ "King Arthur"         │ "Antoine Fuqua"     │
│ 12  │ "Lincoln"             │ "Steven Spielberg"  │
│ 13  │ "Memento"             │ "Christopher Nolan" │
│ 14  │ "Amistad"             │ "Steven Spielberg"  │

julia> 

Applying the unique() function to display the non-duplicate entries:

julia> unique(my_movies_df)
13×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Empire of the Sun"   │ "Steven Spielberg"  │
│ 6   │ "Firelight"           │ "Steven Spielberg"  │
│ 7   │ "Gladiator"           │ "Ridley Scott"      │
│ 8   │ "Hannibal"            │ "Ridley Scott"      │
│ 9   │ "Interstellar"        │ "Christopher Nolan" │
│ 10  │ "Jaws"                │ "Steven Spielberg"  │
│ 11  │ "King Arthur"         │ "Antoine Fuqua"     │
│ 12  │ "Lincoln"             │ "Steven Spielberg"  │
│ 13  │ "Memento"             │ "Christopher Nolan" │

julia> my_movies_df
14×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Empire of the Sun"   │ "Steven Spielberg"  │
│ 6   │ "Firelight"           │ "Steven Spielberg"  │
│ 7   │ "Gladiator"           │ "Ridley Scott"      │
│ 8   │ "Hannibal"            │ "Ridley Scott"      │
│ 9   │ "Interstellar"        │ "Christopher Nolan" │
│ 10  │ "Jaws"                │ "Steven Spielberg"  │
│ 11  │ "King Arthur"         │ "Antoine Fuqua"     │
│ 12  │ "Lincoln"             │ "Steven Spielberg"  │
│ 13  │ "Memento"             │ "Christopher Nolan" │
│ 14  │ "Amistad"             │ "Steven Spielberg"  │

julia>


To make it permanent we need to use the unique!() function (Notice the Bang(!)):

julia> unique!(my_movies_df)
13×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Empire of the Sun"   │ "Steven Spielberg"  │
│ 6   │ "Firelight"           │ "Steven Spielberg"  │
│ 7   │ "Gladiator"           │ "Ridley Scott"      │
│ 8   │ "Hannibal"            │ "Ridley Scott"      │
│ 9   │ "Interstellar"        │ "Christopher Nolan" │
│ 10  │ "Jaws"                │ "Steven Spielberg"  │
│ 11  │ "King Arthur"         │ "Antoine Fuqua"     │
│ 12  │ "Lincoln"             │ "Steven Spielberg"  │
│ 13  │ "Memento"             │ "Christopher Nolan" │

julia> my_movies_df
13×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Empire of the Sun"   │ "Steven Spielberg"  │
│ 6   │ "Firelight"           │ "Steven Spielberg"  │
│ 7   │ "Gladiator"           │ "Ridley Scott"      │
│ 8   │ "Hannibal"            │ "Ridley Scott"      │
│ 9   │ "Interstellar"        │ "Christopher Nolan" │
│ 10  │ "Jaws"                │ "Steven Spielberg"  │
│ 11  │ "King Arthur"         │ "Antoine Fuqua"     │
│ 12  │ "Lincoln"             │ "Steven Spielberg"  │
│ 13  │ "Memento"             │ "Christopher Nolan" │

julia> 

Observe that the original dataset has got modified. It has one less row as the non-unique entry has been removed.


deleterows!()


To delete any set of rows we can use deleterows!() function. 

julia> deleterows!(my_movies_df,[5,6,7])
10×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Hannibal"            │ "Ridley Scott"      │
│ 6   │ "Interstellar"        │ "Christopher Nolan" │
│ 7   │ "Jaws"                │ "Steven Spielberg"  │
│ 8   │ "King Arthur"         │ "Antoine Fuqua"     │
│ 9   │ "Lincoln"             │ "Steven Spielberg"  │
│ 10  │ "Memento"             │ "Christopher Nolan" │

julia> deleterows!(my_movies_df,8:10)
7×2 DataFrames.DataFrame
│ Row │ MovieName             │ Director            │
├─────┼───────────────────────┼─────────────────────┤
│ 1   │ "Amistad"             │ "Steven Spielberg"  │
│ 2   │ "Batman Begins"       │ "Christopher Nolan" │
│ 3   │ "Catch Me If You Can" │ "Steven Spielberg"  │
│ 4   │ "Dunkirk"             │ "Christopher Nolan" │
│ 5   │ "Hannibal"            │ "Ridley Scott"      │
│ 6   │ "Interstellar"        │ "Christopher Nolan" │
│ 7   │ "Jaws"                │ "Steven Spielberg"  │

julia> 

Julia - Language - DataFrames - Sorting

Sometimes, we may want to sort our DataFrame - Ascending / Descending. Sorting the data point values in a column or columns is an important part of Data Analysis. There are much possibilites using sort() function. 

 $ julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-pc-linux-gnu

julia> using DataFrames;

julia> movies_df = DataFrame(Group=rand(["Amistad","Batman Begins","Catch Me If You Can","Dunkirk","Empire of the Sun","Firelight","Gladiator","Hannibal","Interstellar"],15), Variable1 = randn(15), Variable2=rand(15));

julia> movies_df
15×3 DataFrames.DataFrame
│ Row │ Group                 │ Variable1 │ Variable2   │
├─────┼───────────────────────┼───────────┼─────────────┤
│ 1   │ "Gladiator"           │ 1.59811   │ 0.870574    │
│ 2   │ "Batman Begins"       │ -0.735571 │ 0.146472    │
│ 3   │ "Gladiator"           │ -1.18307  │ 0.000210997 │
│ 4   │ "Catch Me If You Can" │ 1.30825   │ 0.813809    │
│ 5   │ "Interstellar"        │ 0.510712  │ 0.428202    │
│ 6   │ "Dunkirk"             │ 0.0282217 │ 0.26914     │
│ 7   │ "Firelight"           │ -0.615212 │ 0.659826    │
│ 8   │ "Interstellar"        │ -2.12227  │ 0.239937    │
│ 9   │ "Firelight"           │ -2.19173  │ 0.643379    │
│ 10  │ "Empire of the Sun"   │ -0.509172 │ 0.548794    │
│ 11  │ "Firelight"           │ -0.485559 │ 0.542529    │
│ 12  │ "Amistad"             │ -1.11516  │ 0.896659    │
│ 13  │ "Dunkirk"             │ -0.647961 │ 0.969086    │
│ 14  │ "Interstellar"        │ -0.785771 │ 0.870153    │
│ 15  │ "Dunkirk"             │ 0.488741  │ 0.697622    │

julia> 

Let us sort this DataFrame. Just observe the below usage of sort!() function. It is having a bang sign(!) in it. This means that, the sort will be made permanent. It is also called as In-Place Sorting when you use the sort!() function.


Sort Decending:


## rev = true will do decending order for numerical values and reverse alphabetical order for strings.

julia> sort!(movies_df,cols=[:Group,:Variable1],rev=true)
15×3 DataFrames.DataFrame
│ Row │ Group                 │ Variable1 │ Variable2   │
├─────┼───────────────────────┼───────────┼─────────────┤
│ 1   │ "Interstellar"        │ 0.510712  │ 0.428202    │
│ 2   │ "Interstellar"        │ -0.785771 │ 0.870153    │
│ 3   │ "Interstellar"        │ -2.12227  │ 0.239937    │
│ 4   │ "Gladiator"           │ 1.59811   │ 0.870574    │
│ 5   │ "Gladiator"           │ -1.18307  │ 0.000210997 │
│ 6   │ "Firelight"           │ -0.485559 │ 0.542529    │
│ 7   │ "Firelight"           │ -0.615212 │ 0.659826    │
│ 8   │ "Firelight"           │ -2.19173  │ 0.643379    │
│ 9   │ "Empire of the Sun"   │ -0.509172 │ 0.548794    │
│ 10  │ "Dunkirk"             │ 0.488741  │ 0.697622    │
│ 11  │ "Dunkirk"             │ 0.0282217 │ 0.26914     │
│ 12  │ "Dunkirk"             │ -0.647961 │ 0.969086    │
│ 13  │ "Catch Me If You Can" │ 1.30825   │ 0.813809    │
│ 14  │ "Batman Begins"       │ -0.735571 │ 0.146472    │
│ 15  │ "Amistad"             │ -1.11516  │ 0.896659    │

julia> 

Sort Ascending:


## rev = false will do ascending order for numerical values and alphabetical order for strings.

julia> sort!(movies_df,cols=[:Group,:Variable1],rev=false)
15×3 DataFrames.DataFrame
│ Row │ Group                 │ Variable1 │ Variable2   │
├─────┼───────────────────────┼───────────┼─────────────┤
│ 1   │ "Amistad"             │ -1.11516  │ 0.896659    │
│ 2   │ "Batman Begins"       │ -0.735571 │ 0.146472    │
│ 3   │ "Catch Me If You Can" │ 1.30825   │ 0.813809    │
│ 4   │ "Dunkirk"             │ -0.647961 │ 0.969086    │
│ 5   │ "Dunkirk"             │ 0.0282217 │ 0.26914     │
│ 6   │ "Dunkirk"             │ 0.488741  │ 0.697622    │
│ 7   │ "Empire of the Sun"   │ -0.509172 │ 0.548794    │
│ 8   │ "Firelight"           │ -2.19173  │ 0.643379    │
│ 9   │ "Firelight"           │ -0.615212 │ 0.659826    │
│ 10  │ "Firelight"           │ -0.485559 │ 0.542529    │
│ 11  │ "Gladiator"           │ -1.18307  │ 0.000210997 │
│ 12  │ "Gladiator"           │ 1.59811   │ 0.870574    │
│ 13  │ "Interstellar"        │ -2.12227  │ 0.239937    │
│ 14  │ "Interstellar"        │ -0.785771 │ 0.870153    │
│ 15  │ "Interstellar"        │ 0.510712  │ 0.428202    │

julia> 

Provding sort attribute to each column:


julia> sort!(movies_df,cols=[:Group,:Variable1,:Variable2],rev=[false,true,true])
15×3 DataFrames.DataFrame
│ Row │ Group                 │ Variable1 │ Variable2   │
├─────┼───────────────────────┼───────────┼─────────────┤
│ 1   │ "Amistad"             │ -1.11516  │ 0.896659    │
│ 2   │ "Batman Begins"       │ -0.735571 │ 0.146472    │
│ 3   │ "Catch Me If You Can" │ 1.30825   │ 0.813809    │
│ 4   │ "Dunkirk"             │ 0.488741  │ 0.697622    │
│ 5   │ "Dunkirk"             │ 0.0282217 │ 0.26914     │
│ 6   │ "Dunkirk"             │ -0.647961 │ 0.969086    │
│ 7   │ "Empire of the Sun"   │ -0.509172 │ 0.548794    │
│ 8   │ "Firelight"           │ -0.485559 │ 0.542529    │
│ 9   │ "Firelight"           │ -0.615212 │ 0.659826    │
│ 10  │ "Firelight"           │ -2.19173  │ 0.643379    │
│ 11  │ "Gladiator"           │ 1.59811   │ 0.870574    │
│ 12  │ "Gladiator"           │ -1.18307  │ 0.000210997 │, Duplicates and NA
│ 13  │ "Interstellar"        │ 0.510712  │ 0.428202    │
│ 14  │ "Interstellar"        │ -0.785771 │ 0.870153    │
│ 15  │ "Interstellar"        │ -2.12227  │ 0.239937    │

julia> 

Sunday, September 10, 2017

Julia - Language - The Split Apply Combine Strategy - by, aggregate, groupby and view functions

A common data analysis task is to split dataset into groups, apply some functions to each group and then combining the results. Hadley Wickham has described this in his paper (http://www.jstatsoft.org/v40/i01) a standardised framework for handling this sort of computation. 

Refer: http://juliadata.github.io/DataFrames.jl/stable/man/split_apply_combine/#The-Split-Apply-Combine-Strategy-1

Julia Language provides the following functions in DataFrames package to handle splicing and grouping of data:

  1. by
  2. aggregate
  3. groupby
  4. view 


by function:


$ julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-pc-linux-gnu

julia> using DataFrames;

julia> city_df = DataFrame(Group=rand(["Agra","Bengaluru","Chandigarh"],15), Variable1 = randn(15), Variable2=rand(15));

julia> city_df
15×3 DataFrames.DataFrame
│ Row │ Group        │ Variable1   │ Variable2  │
├─────┼──────────────┼─────────────┼────────────┤
│ 1   │ "Bengaluru"  │ -0.586793   │ 0.463548   │
│ 2   │ "Chandigarh" │ 1.13232     │ 0.630437   │
│ 3   │ "Bengaluru"  │ 0.309367    │ 0.233593   │
│ 4   │ "Chandigarh" │ -1.17079    │ 0.562659   │
│ 5   │ "Agra"       │ -1.66022    │ 0.923693   │
│ 6   │ "Bengaluru"  │ 0.40091     │ 4.30822e-5 │
│ 7   │ "Bengaluru"  │ -2.5064     │ 0.329671   │
│ 8   │ "Agra"       │ -1.30047    │ 0.78777    │
│ 9   │ "Agra"       │ 0.469538    │ 0.171885   │
│ 10  │ "Chandigarh" │ -0.00710071 │ 0.567401   │
│ 11  │ "Chandigarh" │ 1.1994      │ 0.606546   │
│ 12  │ "Agra"       │ -0.420354   │ 0.658603   │
│ 13  │ "Bengaluru"  │ 0.980519    │ 0.956216   │
│ 14  │ "Chandigarh" │ -1.02094    │ 0.829364   │
│ 15  │ "Agra"       │ -0.799467   │ 0.886456   │

julia> 

Now, let us apply the "by" function. The "by" function takes 3 arguments:
1) DataFrame
2) One or more column names to split the DataFrame on.
3) a function or expression to apply to each subset of the DataFrame

julia> by(city_df,:Group,size)
3×2 DataFrames.DataFrame
│ Row │ Group        │ x1     │
├─────┼──────────────┼────────┤
│ 1   │ "Agra"       │ (5, 3) │
│ 2   │ "Bengaluru"  │ (5, 3) │
│ 3   │ "Chandigarh" │ (5, 3) │

julia> 


We can also use stabby functions as the third parameter:

julia> by(city_df,:Group,stabbyDF1 -> mean(stabbyDF1[:Variable1]))
3×2 DataFrames.DataFrame
│ Row │ Group        │ x1        │
├─────┼──────────────┼───────────┤
│ 1   │ "Agra"       │ -0.742193 │
│ 2   │ "Bengaluru"  │ -0.280479 │
│ 3   │ "Chandigarh" │ 0.0265779 │

julia> 

To get a custom column name for the 3rd column:
julia> by(city_df,:Group,stabbyDF1 -> DataFrame(Mean = mean(stabbyDF1[:Variable1])))
3×2 DataFrames.DataFrame
│ Row │ Group        │ Mean      │
├─────┼──────────────┼───────────┤
│ 1   │ "Agra"       │ -0.742193 │
│ 2   │ "Bengaluru"  │ -0.280479 │
│ 3   │ "Chandigarh" │ 0.0265779 │

julia> 



julia> by(city_df,:Group,stabbyDF2 -> DataFrame(N= size(stabbyDF2,1)))
3×2 DataFrames.DataFrame
│ Row │ Group        │ N │
├─────┼──────────────┼───┤
│ 1   │ "Agra"       │ 5 │
│ 2   │ "Bengaluru"  │ 5 │
│ 3   │ "Chandigarh" │ 5 │

julia> 


We can also use the by function in the following way:

julia> by(city_df, :Group) do stabbyDF3
       DataFrame(Mean = mean(stabbyDF3[:Variable1]), Variance_σ² = var(stabbyDF3[:Variable1]))
       end
3×3 DataFrames.DataFrame
│ Row │ Group        │ Mean      │ Variance_σ² │
├─────┼──────────────┼───────────┼─────────────┤
│ 1   │ "Agra"       │ -0.742193 │ 0.682398    │
│ 2   │ "Bengaluru"  │ -0.280479 │ 1.86272     │
│ 3   │ "Chandigarh" │ 0.0265779 │ 1.28258     │

julia> 

Aggregate function:


Aggregate is the second approach to the Split-Apply-Combine strategy. The aggregate function also groups (spits) the dataframe by a column and then calculates a function or function on the rest of the columns. The new columns are named from the old with an underscore suffix followed by the applied function.

This function takes three arguments:

  1. DataFrame
  2. one or more columns to split the DataFrame on
  3. one or more functions that will be used to compute the summary of each subset of the DataFrame. Each function is applied to each column, that was not used to split the DataFrame, creating new columns of the form $name_$function 


# Calculating the mean and standard deviation of variables 1 and 2 grouped by 
# data point, values in the Group column

julia> aggregate(city_df, :Group, [mean,std,var])
3×7 DataFrames.DataFrame
│ Row │ Group        │ Variable1_mean │ Variable1_std │ Variable1_var │ Variable2_mean │ Variable2_std │ Variable2_var │
├─────┼──────────────┼────────────────┼───────────────┼───────────────┼────────────────┼───────────────┼───────────────┤
│ 1   │ "Agra"       │ -0.742193      │ 0.826074      │ 0.682398      │ 0.685681       │ 0.305001      │ 0.0930255     │
│ 2   │ "Bengaluru"  │ -0.280479      │ 1.36481       │ 1.86272       │ 0.396614       │ 0.355654      │ 0.12649       │
│ 3   │ "Chandigarh" │ 0.0265779      │ 1.13251       │ 1.28258       │ 0.639281       │ 0.109908      │ 0.0120798     │

julia> 


groupby function


The groupby() function simply splits the dataframe as specified:

julia> groupby(city_df,:Group)
DataFrames.GroupedDataFrame  3 groups with keys: Symbol[:Group]
First Group:
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group  │ Variable1 │ Variable2 │
├─────┼────────┼───────────┼───────────┤
│ 1   │ "Agra" │ -1.66022  │ 0.923693  │
│ 2   │ "Agra" │ -1.30047  │ 0.78777   │
│ 3   │ "Agra" │ 0.469538  │ 0.171885  │
│ 4   │ "Agra" │ -0.420354 │ 0.658603  │
│ 5   │ "Agra" │ -0.799467 │ 0.886456  │
⋮
Last Group:
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group        │ Variable1   │ Variable2 │
├─────┼──────────────┼─────────────┼───────────┤
│ 1   │ "Chandigarh" │ 1.13232     │ 0.630437  │
│ 2   │ "Chandigarh" │ -1.17079    │ 0.562659  │
│ 3   │ "Chandigarh" │ -0.00710071 │ 0.567401  │
│ 4   │ "Chandigarh" │ 1.1994      │ 0.606546  │
│ 5   │ "Chandigarh" │ -1.02094    │ 0.829364  │

julia>

To check selected groups:

julia> groupby(city_df,:Group)[1]
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group  │ Variable1 │ Variable2 │
├─────┼────────┼───────────┼───────────┤
│ 1   │ "Agra" │ -1.66022  │ 0.923693  │
│ 2   │ "Agra" │ -1.30047  │ 0.78777   │
│ 3   │ "Agra" │ 0.469538  │ 0.171885  │
│ 4   │ "Agra" │ -0.420354 │ 0.658603  │
│ 5   │ "Agra" │ -0.799467 │ 0.886456  │

julia>

Using for loop with groupby() function:

julia> for i in groupby(city_df,:Group)
       println(i)
       end
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group  │ Variable1 │ Variable2 │
├─────┼────────┼───────────┼───────────┤
│ 1   │ "Agra" │ -1.66022  │ 0.923693  │
│ 2   │ "Agra" │ -1.30047  │ 0.78777   │
│ 3   │ "Agra" │ 0.469538  │ 0.171885  │
│ 4   │ "Agra" │ -0.420354 │ 0.658603  │
│ 5   │ "Agra" │ -0.799467 │ 0.886456  │
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group       │ Variable1 │ Variable2  │
├─────┼─────────────┼───────────┼────────────┤
│ 1   │ "Bengaluru" │ -0.586793 │ 0.463548   │
│ 2   │ "Bengaluru" │ 0.309367  │ 0.233593   │
│ 3   │ "Bengaluru" │ 0.40091   │ 4.30822e-5 │
│ 4   │ "Bengaluru" │ -2.5064   │ 0.329671   │
│ 5   │ "Bengaluru" │ 0.980519  │ 0.956216   │
5×3 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group        │ Variable1   │ Variable2 │
├─────┼──────────────┼─────────────┼───────────┤
│ 1   │ "Chandigarh" │ 1.13232     │ 0.630437  │
│ 2   │ "Chandigarh" │ -1.17079    │ 0.562659  │
│ 3   │ "Chandigarh" │ -0.00710071 │ 0.567401  │
│ 4   │ "Chandigarh" │ 1.1994      │ 0.606546  │
│ 5   │ "Chandigarh" │ -1.02094    │ 0.829364  │

julia> 


View Function:

Using View function we can select certain rows only and therefore splice our DataFrame. Let us now add a third column to our dataframe:

julia> city_df[:Variable3] = randn(15)
15-element Array{Float64,1}:
  1.22496 
 -2.40136 
 -0.270867
  1.43093 
  1.87503 
 -2.10986 
  0.871126
  1.43062 
  1.15698 
 -1.55353 
  0.912977
 -0.222599
  0.281066
 -0.670763
 -0.324178

julia> city_df
15×4 DataFrames.DataFrame
│ Row │ Group        │ Variable1   │ Variable2  │ Variable3 │
├─────┼──────────────┼─────────────┼────────────┼───────────┤
│ 1   │ "Bengaluru"  │ -0.586793   │ 0.463548   │ 1.22496   │
│ 2   │ "Chandigarh" │ 1.13232     │ 0.630437   │ -2.40136  │
│ 3   │ "Bengaluru"  │ 0.309367    │ 0.233593   │ -0.270867 │
│ 4   │ "Chandigarh" │ -1.17079    │ 0.562659   │ 1.43093   │
│ 5   │ "Agra"       │ -1.66022    │ 0.923693   │ 1.87503   │
│ 6   │ "Bengaluru"  │ 0.40091     │ 4.30822e-5 │ -2.10986  │
│ 7   │ "Bengaluru"  │ -2.5064     │ 0.329671   │ 0.871126  │
│ 8   │ "Agra"       │ -1.30047    │ 0.78777    │ 1.43062   │
│ 9   │ "Agra"       │ 0.469538    │ 0.171885   │ 1.15698   │
│ 10  │ "Chandigarh" │ -0.00710071 │ 0.567401   │ -1.55353  │
│ 11  │ "Chandigarh" │ 1.1994      │ 0.606546   │ 0.912977  │
│ 12  │ "Agra"       │ -0.420354   │ 0.658603   │ -0.222599 │
│ 13  │ "Bengaluru"  │ 0.980519    │ 0.956216   │ 0.281066  │
│ 14  │ "Chandigarh" │ -1.02094    │ 0.829364   │ -0.670763 │
│ 15  │ "Agra"       │ -0.799467   │ 0.886456   │ -0.324178 │

julia> 


Using view function, Retrieving rows 5 to 9 in the dataframe:
julia> view(city_df,5:9)
5×4 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group       │ Variable1 │ Variable2  │ Variable3 │
├─────┼─────────────┼───────────┼────────────┼───────────┤
│ 1   │ "Agra"      │ -1.66022  │ 0.923693   │ 1.87503   │
│ 2   │ "Bengaluru" │ 0.40091   │ 4.30822e-5 │ -2.10986  │
│ 3   │ "Bengaluru" │ -2.5064   │ 0.329671   │ 0.871126  │
│ 4   │ "Agra"      │ -1.30047  │ 0.78777    │ 1.43062   │
│ 5   │ "Agra"      │ 0.469538  │ 0.171885   │ 1.15698   │

julia> 

To retrive specific row numbers:

julia> view(city_df,[5,9])
2×4 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group  │ Variable1 │ Variable2 │ Variable3 │
├─────┼────────┼───────────┼───────────┼───────────┤
│ 1   │ "Agra" │ -1.66022  │ 0.923693  │ 1.87503   │
│ 2   │ "Agra" │ 0.469538  │ 0.171885  │ 1.15698   │

julia> 

To evaluate each row:

## the dot in .> indicates that we want to evaluate element by element of each row. This is a nice shortcut in place of "for loop":

julia> view(city_df, city_df[:Variable1] .> 0.5)
3×4 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group        │ Variable1 │ Variable2 │ Variable3 │
├─────┼──────────────┼───────────┼───────────┼───────────┤
│ 1   │ "Chandigarh" │ 1.13232   │ 0.630437  │ -2.40136  │
│ 2   │ "Chandigarh" │ 1.1994    │ 0.606546  │ 0.912977  │
│ 3   │ "Bengaluru"  │ 0.980519  │ 0.956216  │ 0.281066  │

julia> 

If you notice, it went down :Variable1 and returned only those rows which are larger than 0.5.

Lastly, let call the view function to only return rows for "Bengaluru":

julia> view(city_df, city_df[:Group] .== "Bengaluru")
5×4 DataFrames.SubDataFrame{Array{Int64,1}}
│ Row │ Group       │ Variable1 │ Variable2  │ Variable3 │
├─────┼─────────────┼───────────┼────────────┼───────────┤
│ 1   │ "Bengaluru" │ -0.586793 │ 0.463548   │ 1.22496   │
│ 2   │ "Bengaluru" │ 0.309367  │ 0.233593   │ -0.270867 │
│ 3   │ "Bengaluru" │ 0.40091   │ 4.30822e-5 │ -2.10986  │
│ 4   │ "Bengaluru" │ -2.5064   │ 0.329671   │ 0.871126  │
│ 5   │ "Bengaluru" │ 0.980519  │ 0.956216   │ 0.281066  │

julia>