DataFramesMeta.jl¶
I’m going to use R’s dplyr as basis for demonstrating the use of DataFramesMeta.jl. We’ll try to reproduce the example in this site.
Data: nycflights13¶
The data is available in the repository and we’ll import this using the Request.jl.
That is,
using DataFrames
using DataFramesMeta
using Lazy
using Requests
url = "https://raw.githubusercontent.com/alstat/Julia-Data-Query/master/data-raw/flights.csv"
flights = readtable(get_streaming(url));
delete!(flights, :x);
We need to delete the column :x
in the data frame since this is the index
and not a column of the original data. So that,
size(flights)
# (336776,20)
head(flights)
# 6×20 DataFrames.DataFrame
# │ Row │ x │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├─────┼───┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
#
# │ Row │ arr_time │ sched_arr_time │ arr_delay │ carrier │ flight │ tailnum │
# ├─────┼──────────┼────────────────┼───────────┼─────────┼────────┼──────────┤
# │ 1 │ 830 │ 819 │ 11 │ "UA" │ 1545 │ "N14228" │
# │ 2 │ 850 │ 830 │ 20 │ "UA" │ 1714 │ "N24211" │
# │ 3 │ 923 │ 850 │ 33 │ "AA" │ 1141 │ "N619AA" │
# │ 4 │ 1004 │ 1022 │ -18 │ "B6" │ 725 │ "N804JB" │
# │ 5 │ 812 │ 837 │ -25 │ "DL" │ 461 │ "N668DN" │
# │ 6 │ 740 │ 728 │ 12 │ "UA" │ 1696 │ "N39463" │
#
# │ Row │ origin │ dest │ air_time │ distance │ hour │ minute │
# ├─────┼────────┼───────┼──────────┼──────────┼──────┼────────┤
# │ 1 │ "EWR" │ "IAH" │ 227 │ 1400 │ 5 │ 15 │
# │ 2 │ "LGA" │ "IAH" │ 227 │ 1416 │ 5 │ 29 │
# │ 3 │ "JFK" │ "MIA" │ 160 │ 1089 │ 5 │ 40 │
# │ 4 │ "JFK" │ "BQN" │ 183 │ 1576 │ 5 │ 45 │
# │ 5 │ "LGA" │ "ATL" │ 116 │ 762 │ 6 │ 0 │
# │ 6 │ "EWR" │ "ORD" │ 150 │ 719 │ 5 │ 58 │
#
# │ Row │ time_hour │
# ├─────┼───────────────────────┤
# │ 1 │ "2013-01-01 05:00:00" │
# │ 2 │ "2013-01-01 05:00:00" │
# │ 3 │ "2013-01-01 05:00:00" │
# │ 4 │ "2013-01-01 05:00:00" │
# │ 5 │ "2013-01-01 06:00:00" │
# │ 6 │ "2013-01-01 05:00:00" │
Single Table Verbs¶
The following are the equivalen of R dplyr’s functions:
DataFrames.jl and DataFramesMeta.jl | R dplyr |
---|---|
@where |
filter() (and slice() ) |
@orderby |
arrange() |
@select (and rename() , rename!() ) |
select() (and rename() ) |
unique() and unique!() |
distinct() |
@transform |
mutate() (and transmute() ) |
groupby() |
group_by() |
@based_on |
summarise() |
possible but no function yet | sample_n() (and sample_frac() ) |
Filter Rows: @where¶
Subsetting the data according to some conditions is done using the macro @where
.
The first argument is the DataFrame and the succeeding arguments are the conditions
set on the rows of the columns of the DataFrame.
@where flights (:month .== 1) (:day .== 1) # or @where(flights, :month .== 1, :day .== 1)
# 842×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├─────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │ 830 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │ 850 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │ 923 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │ 1004 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │ 812 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │ 740 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │ 913 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │ 709 │
# ⋮
# │ 834 │ 2013 │ 1 │ 1 │ 2327 │ 2250 │ 37 │ 32 │
# │ 835 │ 2013 │ 1 │ 1 │ 2343 │ 1724 │ 379 │ 314 │
# │ 836 │ 2013 │ 1 │ 1 │ 2353 │ 2359 │ -6 │ 425 │
# │ 837 │ 2013 │ 1 │ 1 │ 2353 │ 2359 │ -6 │ 418 │
# │ 838 │ 2013 │ 1 │ 1 │ 2356 │ 2359 │ -3 │ 425 │
# │ 839 │ 2013 │ 1 │ 1 │ NA │ 1630 │ NA │ NA │
# │ 840 │ 2013 │ 1 │ 1 │ NA │ 1935 │ NA │ NA │
# │ 841 │ 2013 │ 1 │ 1 │ NA │ 1500 │ NA │ NA │
# │ 842 │ 2013 │ 1 │ 1 │ NA │ 600 │ NA │ NA │
# more columns ...
This is equivalent to the more verbose code in base Julia’s DataFrame:
flights[(flights[:month] .== 1) & (flights[:day] .== 1), :]
You can also use other boolean operators:
@where flights ((:month .== 1) | (:month .== 2)) # or @where(flights, (:month .== 1) | (:month .== 2))
# 51955×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├───────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │
# ⋮
# │ 51947 │ 2013 │ 2 │ 28 │ NA │ 1820 │ NA │
# │ 51948 │ 2013 │ 2 │ 28 │ NA │ 1154 │ NA │
# │ 51949 │ 2013 │ 2 │ 28 │ NA │ 900 │ NA │
# │ 51950 │ 2013 │ 2 │ 28 │ NA │ 605 │ NA │
# │ 51951 │ 2013 │ 2 │ 28 │ NA │ 850 │ NA │
# │ 51952 │ 2013 │ 2 │ 28 │ NA │ 905 │ NA │
# │ 51953 │ 2013 │ 2 │ 28 │ NA │ 1115 │ NA │
# │ 51954 │ 2013 │ 2 │ 28 │ NA │ 830 │ NA │
# │ 51955 │ 2013 │ 2 │ 28 │ NA │ 840 │ NA │
# more columns ...
To select rows by position, simply do the following:
@where flights 1:10
# 10×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├─────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │ 830 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │ 850 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │ 923 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │ 1004 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │ 812 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │ 740 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │ 913 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │ 709 │
# │ 9 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │ 838 │
# │ 10 │ 2013 │ 1 │ 1 │ 558 │ 600 │ -2 │ 753 │
# more columns ...
or select using boolean index, simply run
@where flights find([true, true])
Arrange Rows: @orderby¶
@orderby flights :year :month :day
# 336776×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├────────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │
# ⋮
# │ 336768 │ 2013 │ 12 │ 31 │ NA │ 2000 │ NA │
# │ 336769 │ 2013 │ 12 │ 31 │ NA │ 1500 │ NA │
# │ 336770 │ 2013 │ 12 │ 31 │ NA │ 1430 │ NA │
# │ 336771 │ 2013 │ 12 │ 31 │ NA │ 855 │ NA │
# │ 336772 │ 2013 │ 12 │ 31 │ NA │ 705 │ NA │
# │ 336773 │ 2013 │ 12 │ 31 │ NA │ 825 │ NA │
# │ 336774 │ 2013 │ 12 │ 31 │ NA │ 1615 │ NA │
# │ 336775 │ 2013 │ 12 │ 31 │ NA │ 600 │ NA │
# │ 336776 │ 2013 │ 12 │ 31 │ NA │ 830 │ NA │
# more columns ...
There is a problem with descending order. The code below is my attempt and the result is not equivalent with that in dplyr.
@orderby flights sort(:arr_delay, rev = false)
Select Columns: @select¶
To select a specific column use @select
macro.
@select flights :year :month :day # or @select(flights, :year, :month, :day)
# 336776×3 DataFrames.DataFrame
# │ Row │ year │ month │ day │
# ├────────┼──────┼───────┼─────┤
# │ 1 │ 2013 │ 1 │ 1 │
# │ 2 │ 2013 │ 1 │ 1 │
# │ 3 │ 2013 │ 1 │ 1 │
# │ 4 │ 2013 │ 1 │ 1 │
# │ 5 │ 2013 │ 1 │ 1 │
# │ 6 │ 2013 │ 1 │ 1 │
# │ 7 │ 2013 │ 1 │ 1 │
# │ 8 │ 2013 │ 1 │ 1 │
# ⋮
# │ 336768 │ 2013 │ 9 │ 30 │
# │ 336769 │ 2013 │ 9 │ 30 │
# │ 336770 │ 2013 │ 9 │ 30 │
# │ 336771 │ 2013 │ 9 │ 30 │
# │ 336772 │ 2013 │ 9 │ 30 │
# │ 336773 │ 2013 │ 9 │ 30 │
# │ 336774 │ 2013 │ 9 │ 30 │
# │ 336775 │ 2013 │ 9 │ 30 │
# │ 336776 │ 2013 │ 9 │ 30 │
Select all columns except those from year to day (inclusive)
cols = setdiff(names(flights), [:year, :month, :day]);
@select flights cols
# 336776×16 DataFrames.DataFrame
# │ Row │ dep_time │ sched_dep_time │ dep_delay │ arr_time │ sched_arr_time │
# ├────────┼──────────┼────────────────┼───────────┼──────────┼────────────────┤
# │ 1 │ 517 │ 515 │ 2 │ 830 │ 819 │
# │ 2 │ 533 │ 529 │ 4 │ 850 │ 830 │
# │ 3 │ 542 │ 540 │ 2 │ 923 │ 850 │
# │ 4 │ 544 │ 545 │ -1 │ 1004 │ 1022 │
# │ 5 │ 554 │ 600 │ -6 │ 812 │ 837 │
# │ 6 │ 554 │ 558 │ -4 │ 740 │ 728 │
# │ 7 │ 555 │ 600 │ -5 │ 913 │ 854 │
# │ 8 │ 557 │ 600 │ -3 │ 709 │ 723 │
# ⋮
# │ 336768 │ 2241 │ 2246 │ -5 │ 2345 │ 1 │
# │ 336769 │ 2307 │ 2255 │ 12 │ 2359 │ 2358 │
# │ 336770 │ 2349 │ 2359 │ -10 │ 325 │ 350 │
# │ 336771 │ NA │ 1842 │ NA │ NA │ 2019 │
# │ 336772 │ NA │ 1455 │ NA │ NA │ 1634 │
# │ 336773 │ NA │ 2200 │ NA │ NA │ 2312 │
# │ 336774 │ NA │ 1210 │ NA │ NA │ 1330 │
# │ 336775 │ NA │ 1159 │ NA │ NA │ 1344 │
# │ 336776 │ NA │ 840 │ NA │ NA │ 1020 │
# more columns ...
You can rename variables with @select
by using named arguments:
@select(flights, tail_num = :tailnum)
# 336776×1 DataFrames.DataFrame
# │ Row │ tail_num │
# ├────────┼──────────┤
# │ 1 │ "N14228" │
# │ 2 │ "N24211" │
# │ 3 │ "N619AA" │
# │ 4 │ "N804JB" │
# │ 5 │ "N668DN" │
# │ 6 │ "N39463" │
# │ 7 │ "N516JB" │
# │ 8 │ "N829AS" │
# ⋮
# │ 336768 │ "N346JB" │
# │ 336769 │ "N565JB" │
# │ 336770 │ "N516JB" │
# │ 336771 │ "N740EV" │
# │ 336772 │ NA │
# │ 336773 │ NA │
# │ 336774 │ "N535MQ" │
# │ 336775 │ "N511MQ" │
# │ 336776 │ "N839MQ" │
But because @select
drops all the variables not explicitly mentioned, it’s not that useful. Instead, use @rename
:
rename(flights, :tailnum, :tail_num)
# 336776×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├────────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │
# ⋮
# │ 336768 │ 2013 │ 9 │ 30 │ 2241 │ 2246 │ -5 │
# │ 336769 │ 2013 │ 9 │ 30 │ 2307 │ 2255 │ 12 │
# │ 336770 │ 2013 │ 9 │ 30 │ 2349 │ 2359 │ -10 │
# │ 336771 │ 2013 │ 9 │ 30 │ NA │ 1842 │ NA │
# │ 336772 │ 2013 │ 9 │ 30 │ NA │ 1455 │ NA │
# │ 336773 │ 2013 │ 9 │ 30 │ NA │ 2200 │ NA │
# │ 336774 │ 2013 │ 9 │ 30 │ NA │ 1210 │ NA │
# │ 336775 │ 2013 │ 9 │ 30 │ NA │ 1159 │ NA │
# │ 336776 │ 2013 │ 9 │ 30 │ NA │ 840 │ NA │
# more columns ...
Extract Distinct Rows: unique()¶
unique(flights, :tailnum)
# 4044×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├──────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │ 830 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │ 850 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │ 923 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │ 1004 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │ 812 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │ 740 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │ 913 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │ 709 │
# ⋮
# │ 4036 │ 2013 │ 9 │ 17 │ 1755 │ 1805 │ -10 │ 1937 │
# │ 4037 │ 2013 │ 9 │ 18 │ 1237 │ 1240 │ -3 │ 1515 │
# │ 4038 │ 2013 │ 9 │ 18 │ 1754 │ 1805 │ -11 │ 1927 │
# │ 4039 │ 2013 │ 9 │ 19 │ 1759 │ 1805 │ -6 │ 1946 │
# │ 4040 │ 2013 │ 9 │ 20 │ 1758 │ 1805 │ -7 │ 1929 │
# │ 4041 │ 2013 │ 9 │ 22 │ 1759 │ 1805 │ -6 │ 1945 │
# │ 4042 │ 2013 │ 9 │ 23 │ 1759 │ 1805 │ -6 │ 1935 │
# │ 4043 │ 2013 │ 9 │ 24 │ 1751 │ 1805 │ -14 │ 1937 │
# │ 4044 │ 2013 │ 9 │ 28 │ 712 │ 720 │ -8 │ 955 │
# more columns ...
unique(flights, [:origin, :dest])
# 224×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├─────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │ 830 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │ 850 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │ 923 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │ 1004 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │ 812 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │ 740 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │ 913 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │ 709 │
# ⋮
# │ 216 │ 2013 │ 3 │ 1 │ 2046 │ 2045 │ 1 │ 2206 │
# │ 217 │ 2013 │ 3 │ 2 │ 1606 │ 1610 │ -4 │ 1723 │
# │ 218 │ 2013 │ 4 │ 14 │ 937 │ 945 │ -8 │ 1130 │
# │ 219 │ 2013 │ 5 │ 25 │ 1007 │ 1000 │ 7 │ 1129 │
# │ 220 │ 2013 │ 6 │ 14 │ 1756 │ 1800 │ -4 │ 2004 │
# │ 221 │ 2013 │ 6 │ 15 │ 1517 │ 1520 │ -3 │ 1656 │
# │ 222 │ 2013 │ 7 │ 5 │ 1355 │ 1400 │ -5 │ 1550 │
# │ 223 │ 2013 │ 7 │ 6 │ 1629 │ 1615 │ 14 │ 1954 │
# │ 224 │ 2013 │ 7 │ 27 │ NA │ 106 │ NA │ NA │
# more columns ...
Add New Column: @transform¶
Besides selecting sets of existing columns, it’s often useful to add new columns that are functions of existing columns. This is the job of @transform:
@transform(flights,
gain = :arr_delay .- :dep_delay,
speed = :distance ./ :air_time * 60)
# 336776×21 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├────────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │
# ⋮
# │ 336768 │ 2013 │ 9 │ 30 │ 2241 │ 2246 │ -5 │
# │ 336769 │ 2013 │ 9 │ 30 │ 2307 │ 2255 │ 12 │
# │ 336770 │ 2013 │ 9 │ 30 │ 2349 │ 2359 │ -10 │
# │ 336771 │ 2013 │ 9 │ 30 │ NA │ 1842 │ NA │
# │ 336772 │ 2013 │ 9 │ 30 │ NA │ 1455 │ NA │
# │ 336773 │ 2013 │ 9 │ 30 │ NA │ 2200 │ NA │
# │ 336774 │ 2013 │ 9 │ 30 │ NA │ 1210 │ NA │
# │ 336775 │ 2013 │ 9 │ 30 │ NA │ 1159 │ NA │
# │ 336776 │ 2013 │ 9 │ 30 │ NA │ 840 │ NA │
# more columns ...
There is no option to refer to columns that was created, and so my solution is
to use another @transform
function.
old_flights = @transform(flights,
gain = :arr_delay .- :dep_delay,
speed = :distance ./ :air_time * 60)
new_flights = @transform(old_flights,
gain_per_hour = :gain ./ (:air_time / 60))
# 336776×22 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │
# ├────────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 517 │ 515 │ 2 │
# │ 2 │ 2013 │ 1 │ 1 │ 533 │ 529 │ 4 │
# │ 3 │ 2013 │ 1 │ 1 │ 542 │ 540 │ 2 │
# │ 4 │ 2013 │ 1 │ 1 │ 544 │ 545 │ -1 │
# │ 5 │ 2013 │ 1 │ 1 │ 554 │ 600 │ -6 │
# │ 6 │ 2013 │ 1 │ 1 │ 554 │ 558 │ -4 │
# │ 7 │ 2013 │ 1 │ 1 │ 555 │ 600 │ -5 │
# │ 8 │ 2013 │ 1 │ 1 │ 557 │ 600 │ -3 │
# ⋮
# │ 336768 │ 2013 │ 9 │ 30 │ 2241 │ 2246 │ -5 │
# │ 336769 │ 2013 │ 9 │ 30 │ 2307 │ 2255 │ 12 │
# │ 336770 │ 2013 │ 9 │ 30 │ 2349 │ 2359 │ -10 │
# │ 336771 │ 2013 │ 9 │ 30 │ NA │ 1842 │ NA │
# │ 336772 │ 2013 │ 9 │ 30 │ NA │ 1455 │ NA │
# │ 336773 │ 2013 │ 9 │ 30 │ NA │ 2200 │ NA │
# │ 336774 │ 2013 │ 9 │ 30 │ NA │ 1210 │ NA │
# │ 336775 │ 2013 │ 9 │ 30 │ NA │ 1159 │ NA │
# │ 336776 │ 2013 │ 9 │ 30 │ NA │ 840 │ NA │
# more columns ...
Summarise Values: @with¶
@with(flights, mean(dropna(:dep_delay)))
# 12.639070257304708
Randomly Sample Rows¶
@where flights sample(1:nrow(flights), 10, replace = false)
# 10×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├─────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 7 │ 1 │ 812 │ 806 │ 6 │ 1049 │
# │ 2 │ 2013 │ 11 │ 10 │ 1356 │ 1400 │ -4 │ 1544 │
# │ 3 │ 2013 │ 5 │ 13 │ 905 │ 900 │ 5 │ 1016 │
# │ 4 │ 2013 │ 11 │ 27 │ 816 │ 805 │ 11 │ 1006 │
# │ 5 │ 2013 │ 5 │ 23 │ 1749 │ 1607 │ 102 │ NA │
# │ 6 │ 2013 │ 11 │ 5 │ 1254 │ 1300 │ -6 │ 1513 │
# │ 7 │ 2013 │ 3 │ 15 │ 601 │ 600 │ 1 │ 723 │
# │ 8 │ 2013 │ 6 │ 9 │ 813 │ 820 │ -7 │ 939 │
# │ 9 │ 2013 │ 6 │ 19 │ 952 │ 909 │ 43 │ 1205 │
# │ 10 │ 2013 │ 5 │ 30 │ 1554 │ 1559 │ -5 │ 1745 │
# more columns ...
@where flights sample(1:nrow(flights), convert(Int64, ceil(nrow(flights) * .01)), replace = false)
# 3368×19 DataFrames.DataFrame
# │ Row │ year │ month │ day │ dep_time │ sched_dep_time │ dep_delay │ arr_time │
# ├──────┼──────┼───────┼─────┼──────────┼────────────────┼───────────┼──────────┤
# │ 1 │ 2013 │ 11 │ 29 │ 1224 │ 1230 │ -6 │ 1401 │
# │ 2 │ 2013 │ 4 │ 7 │ 1753 │ 1755 │ -2 │ 2116 │
# │ 3 │ 2013 │ 5 │ 29 │ 1351 │ 1345 │ 6 │ 1653 │
# │ 4 │ 2013 │ 2 │ 26 │ 942 │ 955 │ -13 │ 1240 │
# │ 5 │ 2013 │ 10 │ 21 │ 811 │ 755 │ 16 │ 929 │
# │ 6 │ 2013 │ 10 │ 5 │ 1416 │ 1415 │ 1 │ 1538 │
# │ 7 │ 2013 │ 5 │ 12 │ 926 │ 930 │ -4 │ 1216 │
# │ 8 │ 2013 │ 5 │ 25 │ 1244 │ 1250 │ -6 │ 1419 │
# ⋮
# │ 3360 │ 2013 │ 4 │ 23 │ 1743 │ 1555 │ 108 │ 1946 │
# │ 3361 │ 2013 │ 10 │ 9 │ 658 │ 700 │ -2 │ 1044 │
# │ 3362 │ 2013 │ 4 │ 28 │ 1455 │ 1038 │ 257 │ 1606 │
# │ 3363 │ 2013 │ 11 │ 25 │ 706 │ 700 │ 6 │ 829 │
# │ 3364 │ 2013 │ 7 │ 7 │ 1626 │ 1555 │ 31 │ 1858 │
# │ 3365 │ 2013 │ 6 │ 24 │ 839 │ 836 │ 3 │ 942 │
# │ 3366 │ 2013 │ 7 │ 11 │ 1441 │ 1440 │ 1 │ 1633 │
# │ 3367 │ 2013 │ 10 │ 23 │ 842 │ 848 │ -6 │ 1116 │
# │ 3368 │ 2013 │ 4 │ 30 │ 1048 │ 1030 │ 18 │ 1351 │
# more columns ...
Grouped Operations¶
by_tailnum = groupby(flights, :tailnum)
delay = @based_on(by_tailnum,
count = length(:tailnum),
dist = mean(dropna(:distance)),
delay = mean(dropna(:arr_delay)))
delay = @where(delay, :count .> 20, :dist .< 2000)
# 2962×4 DataFrames.DataFrame
# │ Row │ tailnum │ count │ dist │ delay │
# ├──────┼──────────┼───────┼─────────┼───────────┤
# │ 1 │ NA │ 2512 │ 710.258 │ NaN │
# │ 2 │ "N0EGMQ" │ 371 │ 676.189 │ 9.98295 │
# │ 3 │ "N10156" │ 153 │ 757.948 │ 12.7172 │
# │ 4 │ "N102UW" │ 48 │ 535.875 │ 2.9375 │
# │ 5 │ "N103US" │ 46 │ 535.196 │ -6.93478 │
# │ 6 │ "N104UW" │ 47 │ 535.255 │ 1.80435 │
# │ 7 │ "N10575" │ 289 │ 519.702 │ 20.6914 │
# │ 8 │ "N105UW" │ 45 │ 524.844 │ -0.266667 │
# ⋮
# │ 2954 │ "N995DL" │ 57 │ 883.579 │ 1.92982 │
# │ 2955 │ "N996AT" │ 29 │ 673.897 │ 6.53846 │
# │ 2956 │ "N996DL" │ 102 │ 897.304 │ 0.524752 │
# │ 2957 │ "N997AT" │ 44 │ 679.045 │ 16.3023 │
# │ 2958 │ "N997DL" │ 63 │ 867.762 │ 4.90323 │
# │ 2959 │ "N998AT" │ 26 │ 593.538 │ 29.96 │
# │ 2960 │ "N998DL" │ 77 │ 857.818 │ 16.3947 │
# │ 2961 │ "N999DN" │ 61 │ 895.459 │ 14.3115 │
# │ 2962 │ "N9EAMQ" │ 248 │ 674.665 │ 9.23529 │
We could use these to find the number of planes and the number of flights that go to each possible destination:
destinations = groupby(flights, :dest)
@based_on(destinations,
planes = length(unique(:tailnum)),
flights = length(:dest)
)
# 105×3 DataFrames.DataFrame
# │ Row │ dest │ planes │ flights │
# ├─────┼───────┼────────┼─────────┤
# │ 1 │ "ABQ" │ 108 │ 254 │
# │ 2 │ "ACK" │ 58 │ 265 │
# │ 3 │ "ALB" │ 172 │ 439 │
# │ 4 │ "ANC" │ 6 │ 8 │
# │ 5 │ "ATL" │ 1180 │ 17215 │
# │ 6 │ "AUS" │ 993 │ 2439 │
# │ 7 │ "AVL" │ 159 │ 275 │
# │ 8 │ "BDL" │ 186 │ 443 │
# ⋮
# │ 97 │ "SRQ" │ 373 │ 1211 │
# │ 98 │ "STL" │ 960 │ 4339 │
# │ 99 │ "STT" │ 87 │ 522 │
# │ 100 │ "SYR" │ 383 │ 1761 │
# │ 101 │ "TPA" │ 1126 │ 7466 │
# │ 102 │ "TUL" │ 105 │ 315 │
# │ 103 │ "TVC" │ 60 │ 101 │
# │ 104 │ "TYS" │ 273 │ 631 │
# │ 105 │ "XNA" │ 176 │ 1036 │
Example of progressively rolling-up a dataset:
daily = groupby(flights, [:year, :month, :day])
per_day = @based_on(daily, flights = length(:day))
# 365×4 DataFrames.DataFrame
# │ Row │ year │ month │ day │ flights │
# ├─────┼──────┼───────┼─────┼─────────┤
# │ 1 │ 2013 │ 1 │ 1 │ 842 │
# │ 2 │ 2013 │ 1 │ 2 │ 943 │
# │ 3 │ 2013 │ 1 │ 3 │ 914 │
# │ 4 │ 2013 │ 1 │ 4 │ 915 │
# │ 5 │ 2013 │ 1 │ 5 │ 720 │
# │ 6 │ 2013 │ 1 │ 6 │ 832 │
# │ 7 │ 2013 │ 1 │ 7 │ 933 │
# │ 8 │ 2013 │ 1 │ 8 │ 899 │
# ⋮
# │ 357 │ 2013 │ 12 │ 23 │ 985 │
# │ 358 │ 2013 │ 12 │ 24 │ 761 │
# │ 359 │ 2013 │ 12 │ 25 │ 719 │
# │ 360 │ 2013 │ 12 │ 26 │ 936 │
# │ 361 │ 2013 │ 12 │ 27 │ 963 │
# │ 362 │ 2013 │ 12 │ 28 │ 814 │
# │ 363 │ 2013 │ 12 │ 29 │ 888 │
# │ 364 │ 2013 │ 12 │ 30 │ 968 │
# │ 365 │ 2013 │ 12 │ 31 │ 776 │
per_month = @based_on(groupby(per_day, [:year, :month]), flights = sum(:flights))
# 12×3 DataFrames.DataFrame
# │ Row │ year │ month │ flights │
# ├─────┼──────┼───────┼─────────┤
# │ 1 │ 2013 │ 1 │ 27004 │
# │ 2 │ 2013 │ 2 │ 24951 │
# │ 3 │ 2013 │ 3 │ 28834 │
# │ 4 │ 2013 │ 4 │ 28330 │
# │ 5 │ 2013 │ 5 │ 28796 │
# │ 6 │ 2013 │ 6 │ 28243 │
# │ 7 │ 2013 │ 7 │ 29425 │
# │ 8 │ 2013 │ 8 │ 29327 │
# │ 9 │ 2013 │ 9 │ 27574 │
# │ 10 │ 2013 │ 10 │ 28889 │
# │ 11 │ 2013 │ 11 │ 27268 │
# │ 12 │ 2013 │ 12 │ 28135 │
per_year = @based_on(groupby(per_day, [:year]), flights = sum(:flights))
# 1×2 DataFrames.DataFrame
# │ Row │ year │ flights │
# ├─────┼──────┼─────────┤
# │ 1 │ 2013 │ 336776 │
Chaining Operations¶
What we’ve done above so far is not based on chaining, in R the popular operator for doing
this is the magrittr’s pipe operator %>%
. In Julia, this is similar to |>
. Consider
the following example from dplyr’s website.
a1 = @select flights :year :month :day :arr_delay :dep_delay
a2 = groupby(a1, [:year, :month, :day])
a3 = @based_on(a2,
arr = mean(dropna(:arr_delay)),
dep = mean(dropna(:dep_delay)))
a4 = @where(a3, (:arr .> 30) | (:dep .> 30))
We can do chaining using |>
with the help of @linq
macro. So that,
@linq flights |>
@select(:year, :month, :day, :arr_delay, :dep_delay) |>
groupby([:year, :month, :day]) |>
based_on(
arr = mean(dropna(:arr_delay)),
dep = mean(dropna(:dep_delay))) |>
@where((:arr .> 30) | (:dep .> 30))
Another alternative, which for me personally is a lot cleaner is to use the pipe
macro @>
from the Lazy.jl package. The above codes is equivalent to
using Lazy: @>
@> begin
flights
@select :year :month :day :arr_delay :dep_delay
groupby([:year, :month, :day])
@based_on(
arr = mean(dropna(:arr_delay)),
dep = mean(dropna(:dep_delay))
)
@where ((:arr .> 30) | (:dep .> 30))
end
The three approaches above returns the same result give below:
# 49×5 DataFrames.DataFrame
# │ Row │ year │ month │ day │ arr │ dep │
# ├─────┼──────┼───────┼─────┼─────────┼─────────┤
# │ 1 │ 2013 │ 1 │ 16 │ 34.2474 │ 24.6129 │
# │ 2 │ 2013 │ 1 │ 31 │ 32.6029 │ 28.6584 │
# │ 3 │ 2013 │ 2 │ 11 │ 36.2901 │ 39.0736 │
# │ 4 │ 2013 │ 2 │ 27 │ 31.2525 │ 37.7633 │
# │ 5 │ 2013 │ 3 │ 8 │ 85.8622 │ 83.5369 │
# │ 6 │ 2013 │ 3 │ 18 │ 41.2919 │ 30.118 │
# │ 7 │ 2013 │ 4 │ 10 │ 38.4123 │ 33.0237 │
# │ 8 │ 2013 │ 4 │ 12 │ 36.0481 │ 34.8384 │
# ⋮
# │ 41 │ 2013 │ 10 │ 7 │ 39.0173 │ 39.1467 │
# │ 42 │ 2013 │ 10 │ 11 │ 18.923 │ 31.2318 │
# │ 43 │ 2013 │ 12 │ 5 │ 51.6663 │ 52.328 │
# │ 44 │ 2013 │ 12 │ 8 │ 36.9118 │ 21.5153 │
# │ 45 │ 2013 │ 12 │ 9 │ 42.5756 │ 34.8002 │
# │ 46 │ 2013 │ 12 │ 10 │ 44.5088 │ 26.4655 │
# │ 47 │ 2013 │ 12 │ 14 │ 46.3975 │ 28.3616 │
# │ 48 │ 2013 │ 12 │ 17 │ 55.8719 │ 40.7056 │
# │ 49 │ 2013 │ 12 │ 23 │ 32.226 │ 32.2541 │