Custom Selectables#
As described in the previous section Selectables
are the tidychef classes that provide the cell selection mechanisms.
Some common selecrables are shown below.
Type |
Description |
---|---|
|
The common class all selectables inherit from. This is where the selection methods all tabulated sources have in common reside |
|
Extends |
|
Extends |
|
Extends |
This document will explain how to ceate your own custom selectables with methods of your own devising.
Source Data#
The data source we’re using for these examples is shown below:
The full data source can be viewed here.
from tidychef import acquire, preview
from tidychef.selection import CsvSelectable
table: CsvSelectable = acquire.csv.http("https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv")
preview(table)
Unnamed Table
A | B | C | D | E | F | G | H | I | J | K | |
1 | |||||||||||
2 | Houses | Cars | Boats | Houses | Cars | Boats | |||||
3 | Beatles | Rolling Stones | |||||||||
4 | John | 1 | 5 | 9 | Keith | 2 | 6 | 10 | |||
5 | Paul | 2 | 6 | 10 | Mick | 3 | 7 | 11 | |||
6 | George | 2 | 7 | 11 | Charlie | 3 | 8 | 12 | |||
7 | Ringo | 4 | 8 | 12 | Ronnie | 5 | 9 | 13 | |||
8 |
Example Scenario 1#
We want to create our very own custom selectable to work with csv sources such as the above.
We want to create a new selection method
column_c()
that explicitly selects cells in column C.
First we’ll create a new class inheriting from Selectable
which we’ll call AwesomeNewSelectable
.
from tidychef.selection.selectable import Selectable
from tidychef.utils.decorators import dontmutate
class AwesomeNewSelectable(Selectable):
@dontmutate
def column_c(self):
"""
Filter down the currently selected cells to only contain
cells that happen to be in column C
"""
# Column C is x offset 2
self.cells = [cell for cell in self.cells if cell.x == 2]
return self
Three key rules are on show above that you need to adhere to it creating your own Selectables:
A new selectable class should always inherit from
Selectable
or something that itself inherits fromSelectable
(egXlsxSelectable
).
A Selectable method must always have a
@dontmutate
decorator.
A selection method must always return self (that’s how the extended dot notation works).
Now lets see our new AwesomeNewSelectable
and column_c
method in action:
from tidychef import acquire, preview
table: AwesomeNewSelectable = acquire.csv.http(
"https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv",
selectable=AwesomeNewSelectable)
# Lets just use the new method then preview it
example1 = table.column_c().label_as("Just Column C")
preview(example1)
# And together with another method to confirm command chaining is working as expected
example1 = table.column_c().is_not_blank().label_as("Non blanks in column C")
preview(example1)
Just Column C |
Unnamed Table
A | B | C | D | E | F | G | H | I | J | K | |
1 | |||||||||||
2 | Houses | Cars | Boats | Houses | Cars | Boats | |||||
3 | Beatles | Rolling Stones | |||||||||
4 | John | 1 | 5 | 9 | Keith | 2 | 6 | 10 | |||
5 | Paul | 2 | 6 | 10 | Mick | 3 | 7 | 11 | |||
6 | George | 2 | 7 | 11 | Charlie | 3 | 8 | 12 | |||
7 | Ringo | 4 | 8 | 12 | Ronnie | 5 | 9 | 13 | |||
8 |
Non blanks in column C |
Unnamed Table
A | B | C | D | E | F | G | H | I | J | K | |
1 | |||||||||||
2 | Houses | Cars | Boats | Houses | Cars | Boats | |||||
3 | Beatles | Rolling Stones | |||||||||
4 | John | 1 | 5 | 9 | Keith | 2 | 6 | 10 | |||
5 | Paul | 2 | 6 | 10 | Mick | 3 | 7 | 11 | |||
6 | George | 2 | 7 | 11 | Charlie | 3 | 8 | 12 | |||
7 | Ringo | 4 | 8 | 12 | Ronnie | 5 | 9 | 13 | |||
8 |
Example Scenario 2#
We want to create another method for
AwesomeNewSelectable
calledgrab_right()
that adds every cell that is dirctly to the right of a selected cell to the current selection.
But first we need to understand about .cells
vs .pcells
.
.cells vs .pcells#
When writing methods for a Selectable
class there are two key attributes to be aware of:
Attribute |
Description |
---|---|
cells |
The currently selected cells. |
pcells |
The prisine cell selection, i.e a list of cells representing the full unfiltered tabular source. |
The principle is to filter down you just filter cells
, but to expand cells
you need to compare your cells
to pcells
.
An example follows:
Example 2#
from tidychef.selection.selectable import Selectable
from tidychef.utils.decorators import dontmutate
class AwesomeNewSelectable(Selectable):
@dontmutate
def column_c(self):
"""
Filter down the currently selected cells to only contain
cells that happen to be in column C
"""
# Column C is x offset 2
self.cells = [cell for cell in self.cells if cell.x == 2]
return self
@dontmutate
def grab_right(self):
"""
Adds every cell that is one cell directly to the right of
a currently selected cell to the selection of cell.
"""
# x is the horizontal axis, so x+1 is "one cell right"
additional_cells = []
for cell in self.cells:
additional = [pcell for pcell in self.pcells if
cell.x+1 == pcell.x and cell.y == pcell.y]
if additional not in self.cells:
additional_cells.append(additional[0])
self.cells += additional_cells
return self
and now lets try it
from tidychef import acquire, preview
table: AwesomeNewSelectable = acquire.csv.http(
"https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv",
selectable=AwesomeNewSelectable)
# Lets just use the new method then preview it
example1 = table.column_c().grab_right().label_as("column_c() selected with grab_right() to also get column D")
preview(example1)
column_c() selected with grab_right() to also get column D |
Unnamed Table
A | B | C | D | E | F | G | H | I | J | K | |
1 | |||||||||||
2 | Houses | Cars | Boats | Houses | Cars | Boats | |||||
3 | Beatles | Rolling Stones | |||||||||
4 | John | 1 | 5 | 9 | Keith | 2 | 6 | 10 | |||
5 | Paul | 2 | 6 | 10 | Mick | 3 | 7 | 11 | |||
6 | George | 2 | 7 | 11 | Charlie | 3 | 8 | 12 | |||
7 | Ringo | 4 | 8 | 12 | Ronnie | 5 | 9 | 13 | |||
8 |