powerjoin
Extensions of 'dplyr' and 'fuzzyjoin' Join Functions
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (13.3%) to scientific vocabulary
Keywords from Contributors
dbi
package-creation
data-model
data-warehousing
datawarehousing
dbplyr
relational-databases
standardization
report
Last synced: 10 months ago
·
JSON representation
Repository
Extensions of 'dplyr' and 'fuzzyjoin' Join Functions
Basic Info
Statistics
- Stars: 109
- Watchers: 3
- Forks: 2
- Open Issues: 11
- Releases: 2
Created over 4 years ago
· Last pushed 11 months ago
Metadata Files
Readme
Changelog
License
README.Rmd
---
output: github_document
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%",
tidy.opts = list(blank = FALSE)
)
options(tidyverse.quiet = TRUE)
```
# powerjoin
{powerjoin} extends {dplyr}'s join functions.
* Make your joins safer with the `check` argument and the `check_specs()`function
* Deal with conflicting column names by combining, coalescing them etc using the `conflict` argument
* Preprocess input, for instance to select columns to join without having to repeat
key columns in the selection
* Do painless fuzzy joins thanks to a generalized `by` argument accepting formulas
* Fill unmatched values using the `fill` argument
* Operate recursive joins by providing lists of data frames to `x` and `y`
* Keep or drop key columns with more flexibility thanks to an enhanced `keep`argument
## Installation
Install CRAN version with:
``` r
install.packages("powerjoin")
```
Or development version with:
``` r
remotes::install_github("moodymudskipper/powerjoin")
```
## Now let's match penguins
```{r}
library(powerjoin)
library(tidyverse)
# toy dataset built from Allison Horst's {palmerpenguins} package and
# Hadley Wickham's {babynames}
male_penguins <- tribble(
~name, ~species, ~island, ~flipper_length_mm, ~body_mass_g,
"Giordan", "Gentoo", "Biscoe", 222L, 5250L,
"Lynden", "Adelie", "Torgersen", 190L, 3900L,
"Reiner", "Adelie", "Dream", 185L, 3650L
)
female_penguins <- tribble(
~name, ~species, ~island, ~flipper_length_mm, ~body_mass_g,
"Alonda", "Gentoo", "Biscoe", 211, 4500L,
"Ola", "Adelie", "Dream", 190, 3600L,
"Mishayla", "Gentoo", "Biscoe", 215, 4750L,
)
```
## Safer joins
The `check` argument receives an object created by the `check_specs()` function,
which provides ways to handle specific input properties, its arguments
can be :
* `"ignore"` : stay silent (default except for `implicit_keys`)
* `"inform"`
* `"warn"`
* `"abort"`
We can print these defaults :
```{r}
check_specs()
```
By default it works like {dplyr}, informing in case of implicit keys, and no
further checks :
```{r, error = TRUE}
power_inner_join(
male_penguins[c("species", "island")],
female_penguins[c("species", "island")]
)
```
We can silence the implicit key detection and check that we have unique keys in
the right table
```{r}
check_specs(implicit_keys = "ignore", duplicate_keys_right = "abort")
```
```{r, error = TRUE}
power_inner_join(
male_penguins[c("species", "island")],
female_penguins[c("species", "island")],
check = check_specs(implicit_keys = "ignore", duplicate_keys_right = "abort")
)
```
The `column_conflict` argument guarantees that you won't have columns renamed without you
knowing, you might need it most of the time, we could setup some development and
production specs for our most common joins:
```{r}
dev_specs <- check_specs(
column_conflict = "abort",
inconsistent_factor_levels = "inform",
inconsistent_type = "inform"
)
prod_specs <- check_specs(
column_conflict = "abort",
implicit_keys = "abort"
)
```
This will save some typing :
```{r, error = TRUE, eval = FALSE}
power_inner_join(
male_penguins,
female_penguins,
by = c("species", "island"),
check = dev_specs
)
#> Error: The following columns are conflicted and their conflicts are not handled:
#> 'name', 'flipper_length_mm', 'body_mass_g'
```
## Handle column conflict
We saw above how to fail when encountering column conflict, here we show how to
handle it.
To resolve conflicts between identically named join columns, set the `conflict`
argument to a 2 argument function (or formula) that will take as arguments the 2 conflicting
joined columns after the join.
```{r}
df1 <- tibble(id = 1:3, value = c(10, NA, 30))
df2 <- tibble(id = 2:4, value = c(22, 32, 42))
power_left_join(df1, df2, by = "id", conflict = `+`)
```
Coalescing is the most common use case and we provide the functions `coalesce_xy()`
and `coalesce_yx()` to ease this task (both wrapped around `dplyr::coalesce()`).
```{r}
power_left_join(df1, df2, by = "id", conflict = coalesce_xy)
power_left_join(df1, df2, by = "id", conflict = coalesce_yx)
```
Note that the function is operating on vectors by default, not rowwise, however
we can make it work rowwise by using `rw` in the lhs of the formula.
```{r}
power_left_join(df1, df2, by = "id", conflict = ~ sum(.x, .y, na.rm = TRUE))
power_left_join(df1, df2, by = "id", conflict = rw ~ sum(.x, .y, na.rm = TRUE))
```
If you need finer control, `conflict` can also be a named list of such functions,
formulas or special values, each to be applied on the relevant pair of conflicted
columns.
## Preprocess inputs
Traditionally key columns need to be repeated when preprocessing inputs
before a join, which is an annoyance and an opportunity for mistakes.
With {powerjoin} we can do :
```{r}
power_inner_join(
male_penguins %>% select_keys_and(name),
female_penguins %>% select_keys_and(female_name = name),
by = c("species", "island")
)
```
For semi joins, just omit arguments to `select_keys_and()`:
```{r}
power_inner_join(
male_penguins,
female_penguins %>% select_keys_and(),
by = c("species", "island")
)
```
We could also aggregate on keys before the join, without the need for any
`group_by()`/`ungroup()` gymnastics :
```{r}
power_left_join(
male_penguins %>% summarize_by_keys(male_weight = mean(body_mass_g)),
female_penguins %>% summarize_by_keys(female_weight = mean(body_mass_g)),
by = c("species", "island")
)
```
`pack_along_keys()` packs given columns, or all non key columns by default, into
a data frame column named by the `name` argument, it's useful to namespace the
data and avoid conflicts
```{r}
power_left_join(
male_penguins %>% pack_along_keys(name = "m"),
female_penguins %>% pack_along_keys(name = "f"),
by = c("species", "island")
)
```
We have more of these, all variants of tidyverse functions :
* `nest_by_keys()` nests given columns, or all by default, if `name` is given
a single list column of data frames is created
* `complete_keys()` expands the key columns, so all combinations are present,
filling the rest of the new rows with `NA`s. Absent factor levels are expanded
as well.
These functions do not modify the data but add an attribute that will be processed
by the join function later on, so no function should be used on top of them.
## Fuzzy joins
To do fuzzy joins we use formulas in the `by` argument, in this formula we use,
`.x` and `.y` to describe the left and right tables. This is very flexible
but can be costly since a cartesian product is computed.
```{r}
power_inner_join(
male_penguins %>% select_keys_and(male_name = name),
female_penguins %>% select_keys_and(female_name = name),
by = c(~.x$flipper_length_mm < .y$flipper_length_mm, ~.x$body_mass_g > .y$body_mass_g)
)
```
We might also mix fuzzy joins with regular joins :
```{r}
power_inner_join(
male_penguins %>% select_keys_and(male_name = name),
female_penguins %>% select_keys_and(female_name = name),
by = c("island", ~.x$flipper_length_mm > .y$flipper_length_mm)
)
```
Finally we might want to create a column with a value used in the comparison,
in that case we will use `<-` in the formula (several times if needed)`:
```{r}
power_inner_join(
male_penguins %>% select_keys_and(male_name = name),
female_penguins %>% select_keys_and(female_name = name),
by = ~ (mass_ratio <- .y$body_mass_g / .x$body_mass_g) > 1.2
)
```
## Fill unmatched values
The `fill` argument is used to specify what to fill unmatched values with,
note that missing values resulting from matches are not replaced.
```{r}
df1 <- tibble(id = 1:3)
df2 <- tibble(id = 1:2, value2 = c(2, NA), value3 = c(NA, 3))
power_left_join(df1, df2, by = "id", fill = 0)
power_left_join(df1, df2, by = "id", fill = list(value2 = 0))
```
## Join recursively
The `x` and `y` arguments accept lists of data frames so one can do :
```{r}
df1 <- tibble(id = 1, a = "foo")
df2 <- tibble(id = 1, b = "bar")
df3 <- tibble(id = 1, c = "baz")
power_left_join(list(df1, df2, df3), by = "id")
power_left_join(df1, list(df2, df3), by = "id")
```
## Enhanced `keep` argument
By default, as in *{dplyr}*, key columns are merged and given names from the
left table. In case of a fuzzy join columns that participate in a fuzzy join are
kept from both sides.
We provide additional values `"left"`, `"right"`, `"both"` and `"none"` to choose
which keys to keep or drop.
## Notes
This package supersedes the {safejoin} package which had an unfortunate homonym on CRAN and
had a suboptimal interface and implementation.
Hadley Wickham, Romain François and David Robinson are credited for their work
in {dplyr} and {fuzzyjoin} since this package contains some code copied from these packages.
Owner
- Name: Antoine Fabri
- Login: moodymudskipper
- Kind: user
- Location: Brussels
- Twitter: antoine_fabri
- Repositories: 13
- Profile: https://github.com/moodymudskipper
GitHub Events
Total
- Issues event: 3
- Watch event: 9
- Issue comment event: 7
- Push event: 69
- Pull request event: 45
- Create event: 1
Last Year
- Issues event: 3
- Watch event: 9
- Issue comment event: 7
- Push event: 69
- Pull request event: 45
- Create event: 1
Committers
Last synced: about 1 year ago
Top Committers
| Name | Commits | |
|---|---|---|
| Antoine Fabri | a****i@g****m | 118 |
| Kirill Müller | k****r | 67 |
| Indrajeet Patil | p****e@g****m | 8 |
| Luís Fonseca | l****a@g****m | 2 |
| Wael Sadek | w****k@g****m | 1 |
Issues and Pull Requests
Last synced: 10 months ago
All Time
- Total issues: 39
- Total pull requests: 56
- Average time to close issues: 2 months
- Average time to close pull requests: 5 days
- Total issue authors: 4
- Total pull request authors: 4
- Average comments per issue: 1.56
- Average comments per pull request: 0.18
- Merged pull requests: 54
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 1
- Pull requests: 44
- Average time to close issues: N/A
- Average time to close pull requests: 6 days
- Issue authors: 1
- Pull request authors: 3
- Average comments per issue: 0.0
- Average comments per pull request: 0.09
- Merged pull requests: 43
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- moodymudskipper (34)
- karldw (2)
- krlmlr (2)
- luispfonseca (1)
Pull Request Authors
- krlmlr (40)
- moodymudskipper (13)
- luisDVA (2)
- luispfonseca (1)
Top Labels
Issue Labels
on hold (1)
Pull Request Labels
Packages
- Total packages: 1
-
Total downloads:
- cran 308 last-month
- Total dependent packages: 0
- Total dependent repositories: 1
- Total versions: 2
- Total maintainers: 1
cran.r-project.org: powerjoin
Extensions of 'dplyr' and 'fuzzyjoin' Join Functions
- Homepage: https://github.com/moodymudskipper/powerjoin
- Documentation: http://cran.r-project.org/web/packages/powerjoin/powerjoin.pdf
- License: MIT + file LICENSE
-
Latest release: 0.1.0
published over 3 years ago
Rankings
Stargazers count: 4.1%
Dependent repos count: 24.2%
Average: 24.4%
Forks count: 27.9%
Dependent packages count: 29.0%
Downloads: 36.5%
Maintainers (1)
Last synced:
11 months ago
Dependencies
DESCRIPTION
cran
- cli * imports
- dplyr * imports
- generics * imports
- glue * imports
- lifecycle * imports
- magrittr * imports
- methods * imports
- purrr * imports
- rlang * imports
- tibble * imports
- tidyr * imports
- tidyselect * imports
- vctrs * imports
- testthat >= 3.0.0 suggests
.github/workflows/R-CMD-check-dev.yaml
actions
- ./.github/workflows/check * composite
- ./.github/workflows/custom/after-install * composite
- ./.github/workflows/custom/before-install * composite
- ./.github/workflows/dep-matrix * composite
- ./.github/workflows/install * composite
- ./.github/workflows/rate-limit * composite
- ./.github/workflows/update-snapshots * composite
- actions/checkout v3 composite
- r-lib/actions/setup-r v2 composite
.github/workflows/R-CMD-check.yaml
actions
- ./.github/workflows/check * composite
- ./.github/workflows/commit * composite
- ./.github/workflows/custom/after-install * composite
- ./.github/workflows/custom/before-install * composite
- ./.github/workflows/git-identity * composite
- ./.github/workflows/install * composite
- ./.github/workflows/pkgdown-build * composite
- ./.github/workflows/pkgdown-deploy * composite
- ./.github/workflows/rate-limit * composite
- ./.github/workflows/roxygenize * composite
- ./.github/workflows/style * composite
- ./.github/workflows/update-snapshots * composite
- actions/checkout v3 composite
.github/workflows/check/action.yml
actions
- actions/upload-artifact main composite
- r-lib/actions/check-r-package v2 composite
.github/workflows/install/action.yml
actions
- ./.github/workflows/get-extra * composite
- r-lib/actions/setup-pandoc v2 composite
- r-lib/actions/setup-r v2 composite
- r-lib/actions/setup-r-dependencies v2 composite
.github/workflows/lock.yaml
actions
- dessant/lock-threads v2 composite
.github/workflows/pkgdown-deploy/action.yml
actions
- nick-fields/retry v2 composite
.github/workflows/pkgdown.yaml
actions
- ./.github/workflows/custom/after-install * composite
- ./.github/workflows/custom/before-install * composite
- ./.github/workflows/git-identity * composite
- ./.github/workflows/install * composite
- ./.github/workflows/pkgdown-build * composite
- ./.github/workflows/pkgdown-deploy * composite
- ./.github/workflows/rate-limit * composite
- actions/checkout v3 composite
.github/workflows/pr-commands.yaml
actions
- actions/checkout v3 composite
- r-lib/actions/pr-fetch master composite
- r-lib/actions/pr-push master composite
- r-lib/actions/setup-r master composite
.github/workflows/revdep.yaml
actions
- actions/checkout v3 composite
- actions/upload-artifact main composite
- r-lib/actions/setup-pandoc v2 composite
.github/workflows/style/action.yml
actions
- actions/cache v3 composite
.github/workflows/update-snapshots/action.yml
actions
- peter-evans/create-pull-request v4 composite
.github/workflows/commit/action.yml
actions
.github/workflows/dep-matrix/action.yml
actions
.github/workflows/dep-matrix-suggests/action.yml
actions
.github/workflows/get-extra/action.yml
actions
.github/workflows/git-identity/action.yml
actions
.github/workflows/pkgdown-build/action.yml
actions
.github/workflows/rate-limit/action.yml
actions
.github/workflows/roxygenize/action.yml
actions