Getting Started#
Installation#
Install the PyPI package:
pip install panda-grove
Alternatively, install the package with pip in
editable
mode from the repo:
pip install -e git+ssh://github.com/fburic/panda-grove.git#egg=panda-grove
Start managing DataFrames with a Collection#
import grove
data = grove.Collection({
'items': 'data/items.csv',
'categories': 'data/categories.csv',
'measurements': 'data/measurements.csv'
})
Retrieving DataFrames can be done through indexing or attribute access syntax. Note that for the latter, Collection class attribute names take precedence.
>>> data['items']
>>> data.items
The indexing syntax allows retrieving multiple DataFrames at once, as a list:
>>> data[['items', 'categories']]
If any name in the list is missing, a GroveError is raised.
Generally, Panda Grove prefers to fail quickly, loudly, and explicitly,
to avoid errors creeping in.
Tip
Creating a Collection using a dictionary of df_names -> filenames
(like in the first example above) has the advantage that the dictionary can be saved
as a YAML / JSON specification, to be reused across pipelines or projects.
E.g.
import yaml
with open('pipeline/input_data.yaml') as spec_file:
data_spec = yaml.load(config_file, Loader=yaml.FullLoader)
data = grove.Collection(data_spec)
Adding new DataFrames#
New DataFrames may be added through both indexing and attribute access syntax (one at a time in both cases). It’s sufficient to just give the path of the file:
>>> data['tests'] = 'data/tests.tsv'
>>> data.planning = df
Inspecting the Collection#
The contents of a Collection can be quickly listed by just printing it:
>>> data
items
========
* cols: 2
* rows: 6
categories
==========
* cols: 2
* rows: 12
measurements
============
* cols: 3
* rows: 15
More technical details can be viewed with the Collection.info() method:
>>> data.info()
Contents: 3 DataFrames
['categories', 'items', 'measurements']
Memory usage
============
DataFrame MiB
items 0.000818
categories 0.001476
measurements 0.001195
TOTAL 0.003489
A quick preview of included DataFrames can be shown with Collection.head():
>>> data.head()
items
========
id description
0 A1 A one
1 A2 A two
categories
==========
id category
0 A1 A
1 A1 one
measurements
============
id value measurement_num
0 A1 1 1
1 A1 2 2
Merge multiple DataFrames#
Data may spread across several tables. Grove will iteratively merge a list of DataFrames.
>>> data.merge(['items', 'categories', 'measurements'], on='id')
id description category value measurement_num
0 A1 A one A 1 1
1 A1 A one A 2 2
2 A1 A one A 1 3
3 A1 A one one 1 1
4 A1 A one one 2 2
5 A1 A one one 1 3
...
If the column to be merge on has the same name in all given DataFrames,
the on argument can be omitted, as with Pandas merge().
There is also a module-level version of the merge function
that works independently of a Collection and may be passed
a list of DataFrame objects.
>>> grove.merge([df1, df2, df3], on='id'])
If column names differ between DataFrames, they are provided as a list.
>>> data.merge(['items', 'categories', 'measurements'],
... on=['id', ['id', 'id_2']])
id description category id_2 value measurement_num
0 A1 A one A A1 1 1
1 A1 A one A A1 2 2
2 A1 A one A A1 1 3
3 A1 A one one A1 1 1
4 A1 A one one A1 2 2
5 A1 A one one A1 1 3
...
This operation is just shorthand for the normal Pandas way to merge multiple DataFrames, (as shown below) but less writing and easier scaling.
pd.merge(
pd.merge(
data['items'], data['categories'], on='id'
),
data['measurements'], left_on='id', right_on='id_2'
)
The general structure for a list of DataFrames [X1, X2, ..., Xn] is
[X1X2_on, X2X3_on, ..., Xn-1Xn_on],
where XiXj_on can be a string (common column),
a pair of strings (left_on, right_on arguments),
or a pair of list of multiple columns to join on.
Since the merge operation is performed iteratively left-to-right,
each XiXj_on specification can use any column in preceding DataFrames,
not just the columns in the adjacent Xi and Xj DataFrames.