The sale knows its product, so who knows the maker? Another table does. join adds that table’s columns to this one, matching rows by the columns that say which rows correspond. products holds a maker for each product it lists.
run(' sales then join products by [product] then pick [product, maker, revenue]')
product
maker
revenue
Widget
Acme
100
Gadget
Globex
120
Doohickey
Initech
120
Widget
Acme
150
Widget
Acme
200
Gadget
Globex
300
Doohickey
Initech
80
Doohickey
Initech
200
Widget
Acme
50
Gadget
Globex
240
Widget
Acme
75
Gadget
Globex
60
Widget
Acme
125
Sprocket
NA
150
Sprocket
NA
90
sales |>join(products, by = product) |>pick(product, maker, revenue)
product
maker
revenue
Widget
Acme
100
Gadget
Globex
120
Doohickey
Initech
120
Widget
Acme
150
Widget
Acme
200
Gadget
Globex
300
Doohickey
Initech
80
Doohickey
Initech
200
Widget
Acme
50
Gadget
Globex
240
Widget
Acme
75
Gadget
Globex
60
Widget
Acme
125
Sprocket
NA
150
Sprocket
NA
90
(sales>> join(products, by = col.product)>> pick(col.product, col.maker, col.revenue))
product
maker
revenue
Widget
Acme
125
Gadget
Globex
60
Doohickey
Initech
200
Widget
Acme
75
Gadget
Globex
240
Doohickey
Initech
80
Widget
Acme
50
Gadget
Globex
300
Doohickey
Initech
120
Widget
Acme
200
Gadget
Globex
120
Widget
Acme
150
Widget
Acme
100
Sprocket
NaN
150
Sprocket
NaN
90
sales then join products by [product] then pick [product, maker, revenue]
The Sprocket rows come back with maker missing. sales sold a product that products has never heard of, so there was no row to bring columns from. The rows themselves stay: a join keeps this table’s rows whether or not they matched, until you say otherwise.
Leave by out and god matches on the column names both tables share, then prints the choice back as a note beside the answer, the way the laws show. It is never silent about a choice you did not make.
Often the two tables call the key different things. A table of sales names its region region, and a table of managers names the same thing area. Say both, with is between them, and this table’s name comes first.
managers <-data.frame(area =c("West", "East", "North"),manager =c("Ada", "Grace", "Alan"),stringsAsFactors =FALSE)run(' sales then join managers by [region] is [area] then pick [region, manager, revenue] then take 3')
region
manager
revenue
West
Ada
100
East
Grace
120
West
Ada
120
sales |>join(managers, by = region == area) |>pick(region, manager, revenue) |>take(3)
The answer keeps region, this table’s name for it, and area does not arrive. The two hold the same value on every row that matched, so bringing both would be bringing one column twice. Keeping this table’s name is what lets the rest of the sentence go on saying region.
is here is the same word the grammar uses for equality everywhere else. R writes == and Python writes ==: R has no bare is, and Python’s is already means something else. That one word is the only difference.
Keys of both kinds mix in one by, separated by commas.
What arrives is a column like any other. The next step groups by manager, a column sales never had.
run(' sales then join managers by [region] is [area] then summarize [total] as total([revenue]) by [manager]')
manager
total
Ada
785
Alan
545
Grace
730
sales |>join(managers, by = region == area) |>summarize(total =total(revenue), by = manager)
manager
total
Ada
785
Alan
545
Grace
730
(sales>> join(managers, by = col.region == col.area)>> summarize(total = total(col.revenue), by = col.manager))
manager
total
Ada
785
Alan
545
Grace
730
What varies between the four joins you may know by name is only this: what happens to a row that found no match. So that is what the argument is called.
unmatched =
Which unmatched rows survive
Called elsewhere
"this" (the default)
this table’s
left join
"none"
neither table’s
inner join
"both"
both tables’
full join
There is no "other", and that is not an omission. A right join is this join with the tables swapped, so it adds no meaning and gets no word.
overlap <-data.frame(product ="Widget", revenue =1,stringsAsFactors =FALSE)collect(sales |>join(overlap, by = product))
Error:
!
illegal: both tables have `revenue`, and a join would bring back two columns of that name. Rename one first, or drop it: `then pick all_but [revenue]`
|
2 | then join overlap by [product]
| ^^^^^^^^^^^^
overlap = pd.DataFrame({"product": ["Widget"], "revenue": [1]})try: collect(sales >> join(overlap, by = col.product))except GodError as refusal:print(refusal)
illegal: both tables have `revenue`, and a join would bring back two columns of that name. Rename one first, or drop it: `then pick all_but [revenue]`
|
2 | then join overlap by [product]
| ^^^^^^^^^^^^
Two more refusals guard the key itself. A join that could never match finds no partner for any row, and that looks like a fact about your data. The key must hold the same kind of thing in both tables:
Error:
!
illegal: `[product]` is text here and a number in `codes`, so the two can never match. Convert one of them first
|
2 | then join codes by [product]
| ^^^^^^^
codes = pd.DataFrame({"product": [1, 2], "code": ["A", "B"]})try: collect(sales >> join(codes, by = col.product))except GodError as refusal:print(refusal)
illegal: `[product]` is text here and a number in `codes`, so the two can never match. Convert one of them first
|
2 | then join codes by [product]
| ^^^^^^^
And the key must exist on both sides. maker belongs to products alone, so asking sales for it is refused, and the refusal names the columns sales does have:
Error:
!
illegal: there is no column called `maker`. The table has: date, region, product, quantity, revenue, cost
|
2 | then join products by [maker]
| ^^^^^
try: collect(sales >> join(products, by = col.maker))except GodError as refusal:print(refusal)
illegal: there is no column called `maker`. The table has: date, region, product, quantity, revenue, cost
|
2 | then join products by [maker]
| ^^^^^
A join is the step where it is hardest to say what the table will hold afterwards, so look before running.
sales |>join(products, by = product) |>show_steps()
show_steps(sales >> join(products, by = col.product))
The arriving table gets a line of its own. The column both tables matched on carries an =, and it carries it twice, once on each side, because it is on both and arrives once rather than as two columns with invented names. What crossed over carries a +.
One thing the checker cannot see is a key that repeats. If the other table holds two rows for one key, your rows multiply, and that is a fact about the rows rather than about the sentence. The checker reads column names and never a row, so this one stays yours to know. The drawing says so on its last line rather than promising a number it cannot know.
When the question is only which rows have a partner over there, nothing needs to arrive at all. The next chapter asks it with a word that cannot multiply anything.