Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
20856a0
Added S3 download
k15z Jun 4, 2020
699adae
Added primary/foreign evaluation
k15z Jun 6, 2020
422fad8
Added inference time
k15z Jun 8, 2020
4a74762
Added dask-based benchmarks
k15z Jun 9, 2020
e79ccb6
Added data directory option
k15z Jun 10, 2020
c1e9cf5
Merge branch 'master' into tracer-benchmarks
k15z Jun 19, 2020
5a251de
Addressed feedback
k15z Jun 19, 2020
af41462
Updated test output layouts
ZhuofanXie Mar 7, 2021
bc21be4
add script for downloading datasets
ZhuofanXie Mar 7, 2021
5b61928
skip non-zip files in the s3 database
ZhuofanXie Mar 7, 2021
f0b841e
temporarily modified core.py for testing w/o installation
ZhuofanXie Mar 8, 2021
f3a79c0
Modified benchmark output formats
ZhuofanXie Mar 21, 2021
38a0651
Fixed div by zero error in PKD
ZhuofanXie Mar 21, 2021
14d60ba
Fixed some div by zero bugs
ZhuofanXie Mar 23, 2021
6e1ff75
Modified output filename
ZhuofanXie Mar 23, 2021
a7cf6c9
changed maximum size to 500mb
ZhuofanXie Apr 11, 2021
4c186e4
speed up table transformations in sampling
ZhuofanXie Apr 11, 2021
ff1bdf8
enabled parallel computing for pre-processing
ZhuofanXie Apr 12, 2021
bdc6e51
skip datasets which does not need sampling
ZhuofanXie Apr 13, 2021
164f606
make data sampler able to drop huge datasets
ZhuofanXie Apr 22, 2021
9dd0149
Fixed code styles
ZhuofanXie May 3, 2021
1400867
Add support for multiple/none primary key predictions
ZhuofanXie May 16, 2021
59a5d44
let FKD take in primary key predictions and only look at relations in…
ZhuofanXie May 16, 2021
f103181
modified default parameters for FKD
ZhuofanXie May 16, 2021
bf4dea0
Let CMD detect empty training tables, in which case we skip the fitti…
ZhuofanXie May 21, 2021
b51d72b
Add linear map detector, and change the feature importance mapping to…
ZhuofanXie Jun 1, 2021
d1deb22
Add testing on one dataset and aggregating multiple test results
ZhuofanXie Jun 7, 2021
75c4f74
disable sampling by default
ZhuofanXie Jun 7, 2021
4c01e9e
Enable multi-threading in column map tests
ZhuofanXie Jun 9, 2021
bc7a0fa
Add the option to choose primitive
ZhuofanXie Jun 19, 2021
712c2bd
Made pylint fixes
ZhuofanXie Jun 19, 2021
4b0ea0a
Provided docstrings for data sampler module
ZhuofanXie Jun 19, 2021
6612039
Merge pull request #1 from ZhuofanXie/primitive-improvements
ZhuofanXie Jun 19, 2021
b984702
Implemented confidence estimate for CMD. And updated the tests accord…
ZhuofanXie Jun 23, 2021
34b837d
Restrict linear maps to sum/diff/avg
ZhuofanXie Jul 6, 2021
cd1cce3
Removed all experimental outputs
ZhuofanXie Jul 13, 2021
1608570
set up APIs and basic solution
ZhuofanXie Jul 19, 2021
cde0f87
Create pipelines for how-lineage detection
ZhuofanXie Jul 20, 2021
32f6f93
Implemented how lineage tests and reorganized benchmark module
ZhuofanXie Jul 23, 2021
7cdedd8
Add benchmark to test and fixed all code styles
ZhuofanXie Jul 29, 2021
77f6445
Merge branch 'master' into how-lineage
ZhuofanXie Aug 4, 2021
9927145
Merge pull request #2 from ZhuofanXie/how-lineage
ZhuofanXie Aug 4, 2021
9bba022
Fix a false unused import (which is implicitly used later)
ZhuofanXie Aug 4, 2021
6cae6eb
Updated pretrained primitives
ZhuofanXie Aug 4, 2021
a67444b
Updated documentations and author list
ZhuofanXie Aug 4, 2021
718f58c
Add a placeholder how lineage test
ZhuofanXie Aug 4, 2021
07de445
Implemented composite primary key finder
ZhuofanXie Oct 3, 2021
13e6c6c
partially implemented the composite primary key benchmark (just a fra…
ZhuofanXie Oct 4, 2021
af5d722
Implemented composite primary keys benchmarks. Not tested yet.
ZhuofanXie Oct 4, 2021
ce35dad
fully implemented benchmarks for composite primary keys
ZhuofanXie Oct 4, 2021
bede773
Change the default param of composite primary key finder
ZhuofanXie Oct 4, 2021
0d5d30b
Changed detect UCC hyperparam for speed purpose
ZhuofanXie Oct 4, 2021
0353d69
Add a timeout controller to composite primary key finders
ZhuofanXie Oct 4, 2021
9511710
Fix benchmark behavior on None output
ZhuofanXie Oct 4, 2021
6797219
Fixed typo in benchmark aggregate method
ZhuofanXie Oct 5, 2021
ec2b0bd
Implemented composite foreign key solver
ZhuofanXie Oct 10, 2021
c75e7e6
Fixed bugs in compositeFKD
ZhuofanXie Oct 10, 2021
7c69c5b
Implemented benchmark for composite FKD
ZhuofanXie Oct 10, 2021
0ce86f6
Implemented column mapping detection primitive compatible with compos…
ZhuofanXie Oct 18, 2021
6df6bce
Implemented Composite Col Map benchmark
ZhuofanXie Oct 19, 2021
5ed5f5d
Implemented earth moving distance feature
ZhuofanXie Oct 30, 2021
8174e3c
fixed how lineage testing repeated col name bug
ZhuofanXie Nov 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Credits
* Felipe Hofmann <fealho@mit.edu>
* Kevin Alex Zhang <kevz@mit.edu>
* Carles Sala <csala@pythiac.com>
* Zhuofan Xie <zhuofanx@mit.edu>
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ install-develop: clean-build clean-pyc ## install the package in editable mode a
.PHONY: lint
lint: ## check style with flake8 and isort
flake8 datatracer tests
isort -c --recursive datatracer tests
isort -c --recursive datatracer tests benchmark

.PHONY: fix-lint
fix-lint: ## fix lint issues using autoflake, autopep8, and isort
find datatracer tests -name '*.py' | xargs autoflake --in-place --remove-all-unused-imports --remove-unused-variables
autopep8 --in-place --recursive --aggressive datatracer tests
isort --apply --atomic --recursive datatracer tests
find datatracer tests benchmark -name '*.py' | xargs autoflake --in-place --remove-all-unused-imports --remove-unused-variables
autopep8 --in-place --recursive --aggressive datatracer tests benchmark
isort --apply --atomic --recursive datatracer tests benchmark


# TEST TARGETS
Expand Down
55 changes: 55 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Benchmarking DataTracer
This directory contains code for benchmarking the performance of `DataTracer`
on user-supplied datasets. The datasets for benchmarking can be found in the
`s3://tracer-data` bucket.

<p align="center">
<img src="benchmark.gif"/>
</p>

Each benchmark - `primary`, `foreign`, and `column` - can be executed by
running the following command

> datatracer-benchmark <BENCHMARK_TYPE> --csv /path/to/results.csv

which will (optionally) generate a CSV file with the benchmark results.

## Primary Key
Primary key detection is evaluated by:

- Accuracy. The percent of tables where the primary key was correctly identified.
- Inference time. The amount of time to infer the primary key for all tables.

We will use leave-one-out validation and report the test performance on each dataset
in the S3 bucket.

## Foreign Key
Foreign key detection is evaluated by:

- F1.
- Recall.
- Precision.
- Inference time.

Note that this assumes that the foreign key primitive returns a set of foreign keys;
in other words, for models that return a score for each candidate foreign key, this
assumes that thresholding is done.

We will use leave-one-out validation and report the test performance on each dataset
in the S3 bucket.

## Column Map
Column map detection is evaluated by:

- F1.
- Recall.
- Precision.
- Inference time.

Note that this assumes that the column map primitive returns a set of columns that it
thinks contributed to the target derived column. Since each dataset can have multiple
derived columns, this will report a F1/recall/precision/time tuple for each derived
column in the dataset.

We will use leave-one-out validation and report the test performance on each dataset
in the S3 bucket.
Binary file added benchmark/benchmark.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
223 changes: 223 additions & 0 deletions benchmark/benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import argparse
import os
from io import BytesIO
from time import ctime
from urllib.parse import urljoin
from urllib.request import urlopen
from zipfile import ZipFile

import boto3
import pandas as pd

from column_map_benchmark import benchmark_column_map
from foreign_key_benchmark import benchmark_foreign_key
from how_lineage_benchmark import benchmark_how_lineage
from primary_key_benchmark import benchmark_primary_key
from primary_key_composite_benchmark import benchmark_primary_key_composite
from foreign_key_composite_benchmark import benchmark_foreign_key_composite
from column_map_composite_benchmark import benchmark_composite_column_map

BUCKET_NAME = 'tracer-data'
DATA_URL = 'http://{}.s3.amazonaws.com/'.format(BUCKET_NAME)


def download(data_dir):
"""Download benchmark datasets from S3.

This downloads the benchmark datasets from S3 into the target folder in an
uncompressed format. It skips datasets that have already been downloaded.

Please make sure an appropriate S3 credential is installed before you call
this method.

Args:
data_dir: The directory to download the datasets to.

Returns:
A DataFrame describing the downloaded datasets.

Raises:
NoCredentialsError: If AWS S3 credentials are not found.
"""
rows = []
client = boto3.client('s3')
for dataset in client.list_objects(Bucket=BUCKET_NAME)['Contents']:
if not '.zip' in dataset['Key']:
continue
rows.append(dataset)
dataset_name = dataset['Key'].replace(".zip", "")
dataset_path = os.path.join(data_dir, dataset_name)
if os.path.exists(dataset_path):
dataset["Status"] = "Skipped"
print("Skipping %s" % dataset_name)
else:
dataset["Status"] = "Downloaded"
print("Downloading %s" % dataset_name)
with urlopen(urljoin(DATA_URL, dataset['Key'])) as fp:
with ZipFile(BytesIO(fp.read())) as zipfile:
zipfile.extractall(dataset_path)
return pd.DataFrame(rows)


def start_with(target, source):
return len(source) <= len(target) and target[:len(source)] == source


def aggregate(cmd_name):
cmd_abbrv = {'column': 'ColMap_st',
'foreign': 'ForeignKey_st',
'primary': 'PrimaryKey_st',
'primary_composite': 'CompositePrimaryKey_st',
'foreign_composite': 'CompositeForeignKey_st',
'column_composite': 'CompositeColMap_st',
'how': 'HowLineage_st'
}
if cmd_name not in cmd_abbrv:
print("Invalid command name!")
return None # invalid command name
cmd_name = cmd_abbrv[cmd_name]
dfs = []
for file in os.listdir("Reports"):
if start_with(file, cmd_name):
dfs.append(pd.read_csv("Reports/" + file))
if len(dfs) == 0:
print("No available test results!")
return None
df = pd.concat(dfs, axis=0, ignore_index=True)
os.system("rm Reports/" + cmd_name + "*") # Clean up the caches
return df


def _get_parser():
shared_args = argparse.ArgumentParser(add_help=False)
shared_args.add_argument('--data_dir', type=str,
default=os.path.expanduser("~/tracer_data"), required=False,
help='Path to the benchmark datasets.')
default_csv = "report_" + ctime().replace(" ", "_") + ".csv"
default_csv = default_csv.replace(":", "_")
shared_args.add_argument('--csv', type=str,
default=os.path.expanduser(default_csv), required=False,
help='Path to the CSV file where the report will be written.')
shared_args.add_argument('--ds_name', type=str,
default=None, required=False,
help='Name of the dataset to test on. Default is all available datasets.')
shared_args.add_argument('--problem', type=str,
default=None, required=False,
help='Name of the tests results to aggregate.')
shared_args.add_argument('--primitive', type=str,
default=None, required=False,
help='Name of the primitive to be tested.')

parser = argparse.ArgumentParser(
prog='datatracer-benchmark',
description='DataTracer Benchmark CLI'
)

command = parser.add_subparsers(title='command', help='Command to execute')
parser.set_defaults(benchmark=None)

subparser = command.add_parser(
'download',
parents=[shared_args],
help='Download datasets from S3.'
)
subparser.set_defaults(command=download)

subparser = command.add_parser(
'primary',
parents=[shared_args],
help='Primary key benchmark.'
)
subparser.set_defaults(command=benchmark_primary_key)

subparser = command.add_parser(
'primary_composite',
parents=[shared_args],
help='Composite Primary key benchmark.'
)
subparser.set_defaults(command=benchmark_primary_key_composite)

subparser = command.add_parser(
'foreign_composite',
parents=[shared_args],
help='Composite Foreign key benchmark.'
)
subparser.set_defaults(command=benchmark_foreign_key_composite)

subparser = command.add_parser(
'foreign',
parents=[shared_args],
help='Foreign key benchmark.'
)
subparser.set_defaults(command=benchmark_foreign_key)

subparser = command.add_parser(
'column',
parents=[shared_args],
help='Column map benchmark.'
)
subparser.set_defaults(command=benchmark_column_map)

subparser = command.add_parser(
'column_composite',
parents=[shared_args],
help='Composite column map benchmark.'
)
subparser.set_defaults(command=benchmark_composite_column_map)

subparser = command.add_parser(
'how',
parents=[shared_args],
help='How lineage benchmark.'
)
subparser.set_defaults(command=benchmark_how_lineage)

subparser = command.add_parser(
'aggregate',
parents=[shared_args],
help='Aggregate separate test results.'
)
subparser.set_defaults(command=aggregate)

return parser


def main():
parser = _get_parser()
args = parser.parse_args()
if args.command == download:
df = args.command(args.data_dir)
elif args.command == aggregate:
df = args.command(args.problem)
else:
if args.primitive is None:
df = args.command(args.data_dir, args.ds_name)
else:
df = args.command(args.data_dir, args.ds_name, solver=args.primitive)
cmd_abbrv = {'column': 'ColMap_',
'foreign': 'ForeignKey_',
'primary': 'PrimaryKey_',
'primary_composite': 'CompositePrimaryKey_',
'foreign_composite': 'CompositeForeignKey_',
'column_composite': 'CompositeColMap_',
'how': 'HowLineage_'
}
cmd_str = {benchmark_column_map: 'ColMap_',
benchmark_foreign_key: 'ForeignKey_',
benchmark_primary_key: 'PrimaryKey_',
benchmark_how_lineage: 'HowLineage_',
benchmark_primary_key_composite: 'CompositePrimaryKey_',
benchmark_foreign_key_composite: 'CompositeForeignKey_',
benchmark_composite_column_map: 'CompositeColMap_',
aggregate: cmd_abbrv[args.problem] if args.problem in cmd_abbrv else ''
}
csv_name = "st_" + args.ds_name + ".csv" if args.ds_name else args.csv
# st is for recognition in the aggregation step

if csv_name and (args.command in cmd_str) and (df is not None):
df.to_csv("Reports/" + cmd_str[args.command] + csv_name, index=False)
print(df)


if __name__ == "__main__":
main()
Loading