Skip to content

Commit

Permalink
Merge pull request #92 from KamitaniLab/kvs
Browse files Browse the repository at this point in the history
key-value-store implemented on sqlite3
  • Loading branch information
ShuntaroAoki authored Jul 29, 2024
2 parents 04c506d + f064c9b commit a9ec4dd
Show file tree
Hide file tree
Showing 2 changed files with 454 additions and 0 deletions.
273 changes: 273 additions & 0 deletions bdpy/dataform/kvs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
"""Key-value store."""


from typing import List, Tuple, Union, Optional

import os
import sqlite3
from pathlib import Path

import numpy as np


_array_t = np.ndarray
_path_t = Union[str, Path]


class BaseKeyValueStore(object):
"""Base class for key-value store."""

def get(self, **kwargs) -> _array_t:
raise NotImplementedError("get should be implemented in the subclass.")

def set(self, value: _array_t, **kwargs) -> None:
raise NotImplementedError("set should be implemented in the subclass.")


class SQLite3KeyValueStore(BaseKeyValueStore):
"""Key-value store implemented on SQLite3."""

def __init__(self, path: _path_t, keys: Optional[List[str]] = None):
"""Initialize the SQLite3KeyValueStore object.
Parameters
----------
path: _path_t
The path to the SQLite database file.
keys: List[str], optional
The list of keys. Defaults to [].
"""
if keys is None:
keys = []
self._path = path
self._keys = keys

new_db = not os.path.exists(self._path)

# Connect to DB
self._conn = sqlite3.connect(self._path)

# Enable foreign key
cursor = self._conn.cursor()
cursor.execute("PRAGMA foreign_keys = true")
cursor.close()

# Initialize DB
if new_db:
self._init_empty_db()
if not keys:
raise ValueError("Keys must be given when creating a new database.")
# Add keys
for key in keys:
self._add_key(key)
else:
self._validate_db(keys)
self._keys = self._get_keys()

def _init_empty_db(self) -> None:
"""Create empty tables."""
sqls = [
"""
CREATE TABLE IF NOT EXISTS key_names (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE
)
""",
"""
CREATE TABLE IF NOT EXISTS key_instances (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
key_name_id INTEGER,
FOREIGN KEY (key_name_id) REFERENCES key_names(id)
)
""",
"""
CREATE TABLE IF NOT EXISTS key_value_store (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value BLOB
)
""",
"""
CREATE TABLE IF NOT EXISTS key_group_members (
key_value_store_id INTEGER,
key_instance_id INTEGER,
PRIMARY KEY (key_value_store_id, key_instance_id),
FOREIGN KEY (key_value_store_id) REFERENCES key_value_store(id),
FOREIGN KEY (key_instance_id) REFERENCES key_instances(id)
)
""",
]
cursor = self._conn.cursor()
for sql in sqls:
cursor.execute(sql)
self._conn.commit()
cursor.close()
return None

def _validate_db(self, keys: List[str]) -> None:
pass

def _add_key(self, key: str) -> None:
sql = f"INSERT OR IGNORE INTO key_names (name) VALUES('{key}')"
cursor = self._conn.cursor()
cursor.execute(sql)
self._conn.commit()
cursor.close()
return None

def _get_keys(self) -> List[str]:
sql = "SELECT name FROM key_names"
cursor = self._conn.cursor()
cursor.execute(sql)
res = cursor.fetchall()
cursor.close()
return [r[0] for r in res]

def set(self, value: _array_t, **kwargs) -> None:
"""Set value for the given keys."""
# Check if the keys are valid
for key in kwargs.keys():
if key not in self._keys:
raise ValueError(f"Key '{key}' is not defined.")

# Check if all keys are given
if len(kwargs) != len(self._keys):
raise ValueError("All keys must be given.")

# Check if the given keys already exist
key_group_id = self._get_group_id(**kwargs)
if key_group_id is None:
# Add new key-value pair
sql = "INSERT INTO key_value_store (value) VALUES (?)"
cursor = self._conn.cursor()
cursor.execute(sql, (value.astype(float).tobytes(order='C'),))
key_group_id = cursor.lastrowid
self._conn.commit()
cursor.close()
self._add_group_id(key_group_id, **kwargs)
else:
# Update existing key-value pair
sql = f"""
UPDATE key_value_store
SET value = ?
WHERE id = {key_group_id}
"""
cursor = self._conn.cursor()
cursor.execute(sql, (value.astype(float).tobytes(order='C'),))
self._conn.commit()
cursor.close()

return None

def get(self, **kwargs) -> Optional[_array_t]:
"""Get value for the given keys."""
key_group_id = self._get_group_id(**kwargs)
if key_group_id is None:
return None
sql = f"""
SELECT key_value_store.value FROM key_value_store
WHERE key_value_store.id = {key_group_id}
"""
cursor = self._conn.cursor()
cursor.execute(sql)
res = cursor.fetchall()
cursor.close()
if len(res) == 0:
return None
elif len(res) > 1:
raise ValueError("Multiple values found.")
else:
return np.frombuffer(res[0][0], dtype=float)

def _add_group_id(self, key_group_id: int, **kwargs) -> int:
"""Add group ID."""
for key, inst in kwargs.items():
# Add key instance if not exists
key_instance_id = self._get_key_instance_id(key, inst)
if key_instance_id is not None:
continue
key_name_id = self._get_key_name_id(key)
sql = f"""
INSERT OR IGNORE INTO key_instances (name, key_name_id) VALUES ('{inst}', {key_name_id})
"""
cursor = self._conn.cursor()
cursor.execute(sql)
cursor.close()

inst_ids = [self._get_key_instance_id(key, inst) for key, inst in kwargs.items()]
kvid = key_group_id
sqls = [
f"INSERT INTO key_group_members (key_value_store_id, key_instance_id) VALUES ({kvid}, {inst_id})"
for inst_id in inst_ids
]
cursor = self._conn.cursor()
for sql in sqls:
cursor.execute(sql)
self._conn.commit()
cursor.close()
return key_group_id

def _get_group_id(self, **kwargs) -> Optional[int]:
"""Get group ID."""
where = ' AND '.join(
[
f"""
EXISTS(
SELECT * FROM key_group_members AS kgm{i}
JOIN key_instances AS ki{i} ON kgm{i}.key_instance_id = ki{i}.id
JOIN key_names AS kn{i} ON ki{i}.key_name_id = kn{i}.id
WHERE
kgm.key_value_store_id = kgm{i}.key_value_store_id
AND
kn{i}.name = '{key}' AND ki{i}.name = '{inst}'
)
"""
for i, (key, inst) in enumerate(kwargs.items())
]
)

sql = f"""
SELECT kgm.key_value_store_id
FROM key_group_members AS kgm
JOIN key_instances ON kgm.key_instance_id = key_instances.id
JOIN key_names ON key_instances.key_name_id = key_names.id
WHERE
{where}
GROUP BY kgm.key_value_store_id
"""
cursor = self._conn.cursor()
cursor.execute(sql)
res = cursor.fetchall()
cursor.close()
if len(res) == 0:
# Not found
return None
elif len(res) > 1:
raise ValueError("Multiple key groups found.")
else:
return res[0][0]

def _get_key_name_id(self, key: str) -> int:
"""Get key name ID."""
sql = f"SELECT id FROM key_names WHERE name = '{key}'"
cursor = self._conn.cursor()
cursor.execute(sql)
res = cursor.fetchall()
cursor.close()
if not res:
raise ValueError(f"Key '{key}' is not defined.")
return res[0][0]

def _get_key_instance_id(self, key: str, inst: str) -> Optional[int]:
"""Get key instance ID."""
key_name_id = self._get_key_name_id(key)
sql = f"SELECT id FROM key_instances WHERE name = '{inst}' AND key_name_id = {key_name_id}"
cursor = self._conn.cursor()
cursor.execute(sql)
res = cursor.fetchall()
cursor.close()
if not res:
return None
return res[0][0]

Loading

4 comments on commit a9ec4dd

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
bdpy/bdata
   bdata.py39931920%73–79, 86, 90, 95, 99, 104, 109, 113, 118, 122, 132–134, 155–172, 190, 206–207, 232–248, 252–262, 276–277, 293, 310, 314, 318–356, 388–477, 508, 539, 547–551, 560, 577–584, 589–598, 604–614, 618, 622–625, 628, 632–653, 656–665, 668–677, 683–691, 696–729, 735–744, 750–757, 761–767, 771–799, 803–824, 828–862, 866–868, 872–874, 878–887
   featureselector.py645711%43–47, 52–93, 98–124
   metadata.py674828%21–30, 34, 38, 42, 46, 50, 54, 74–115, 135–144, 149, 154
   utils.py1131039%44–110, 127–173, 201, 217–253, 258, 263
bdpy/dataform
   datastore.py1078521%59–75, 90–93, 97–98, 102–113, 116–119, 122–127, 131–132, 137–158, 190–197, 222–259, 262–265
   features.py29823322%29–32, 41–47, 74–103, 107, 111, 115, 119, 137–163, 168–197, 213–238, 241–269, 272–275, 278–289, 305–319, 323, 327, 331, 335, 339, 343, 347, 351, 355, 359, 364–394, 398–418, 422–462, 465, 470–477, 491–493, 496–499, 502–505, 508–512, 515–516, 536–549
   kvs.py1401400%4–272
   pd.py9544%25–27, 43–44
   sparse.py675124%19–29, 35–46, 52–58, 65–74, 78, 81–87, 90–93, 96–98, 101–126
   utils.py12120%3–18
bdpy/dataset
   utils.py45450%3–98
bdpy/distcomp
   distcomp.py927815%20–29, 32–49, 52–70, 73–93, 96–107, 111, 114–117, 121–127
bdpy/dl
   caffe.py60600%4–129
bdpy/dl/torch
   base.py432444%31–41, 48, 54, 60, 63, 73–83, 90, 96, 102, 105
   models.py33329811%28–84, 114–140, 148–169, 175–238, 249–253, 259–279, 288–292, 297–316, 327–331, 340–405, 427–431, 442–494, 515–517, 528–587, 611–614, 625–684, 708–711, 722–771, 790–793, 804–853, 872–875
   torch.py1098423%40–57, 60, 77–95, 100, 107, 110, 115, 122, 125, 172–202, 205, 208–220, 223–258
bdpy/evals
   metrics.py95878%12–34, 40–73, 82–112, 118–159, 172–179
bdpy/feature
   feature.py30287%33–74
bdpy/fig
   __init__.py440%6–9
   draw_group_image_set.py90900%3–182
   fig.py88880%16–164
   makeplots.py3363360%1–729
   tile_images.py59590%1–193
bdpy/ml
   crossvalidation.py59548%34–61, 104–128, 138, 164–196
   ensemble.py13931%33–46
   learning.py30826414%43–44, 48, 52, 59, 91–104, 109–125, 128, 158–170, 184–209, 260–284, 290–433, 436–461, 465–504, 507–508, 524–536, 541–613
   model.py14012014%29–39, 54–70, 86–144, 156–169, 184–222, 225, 230–250, 254–258, 271–285
   regress.py11827%29–38
   searchlight.py161319%32–51
bdpy/mri
   fmriprep.py4974519%25–34, 38, 44–62, 65–75, 78–89, 92–160, 163–194, 230–360, 367–380, 384, 388–390, 394, 398–400, 410–434, 437–454, 457–464, 471–472, 475–491, 494, 498, 502–815, 819–831, 842–862
   glm.py403610%46–95
   image.py241921%29–54
   load_epi.py281836%36–50, 56–63, 82–88
   load_mri.py191616%16–36
   roi.py2482346%37–100, 122–148, 165–235, 241–314, 320–387, 399–466, 473–499
   spm.py15813912%26–155, 162–166, 170, 174–179, 183–300
bdpy/opendata
   __init__.py110%1
   openneuro.py2102100%1–329
bdpy/pipeline
   config.py362919%15–64
bdpy/preproc
   interface.py524415%31–40, 60–69, 96–105, 111–123, 148–157, 208–217
   preprocessor.py12910717%35, 43–65, 74–78, 85–97, 104–132, 138–189, 196–227, 234–239
   select_top.py231822%35–60
   util.py6267%14, 22
bdpy/recon
   utils.py55550%4–146
bdpy/recon/torch
   __init__.py110%1
   icnn.py1611610%15–478
bdpy/stats
   corr.py433812%29–77, 96–112
bdpy/util
   info.py473623%19–79
   math.py131023%23–38
   utils.py362628%48–66, 93–96, 116–121, 137–145
TOTAL5068445312% 

Tests Skipped Failures Errors Time
1 0 💤 0 ❌ 1 🔥 3.654s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
bdpy/bdata
   bdata.py40032020%73–79, 86, 90, 95, 99, 104, 109, 113, 118, 122, 132–134, 155–172, 190, 206–207, 232–248, 252–262, 276–277, 293, 310, 314, 318–356, 388–477, 508, 539, 547–551, 560, 577–584, 589–598, 604–614, 618, 622–625, 628, 632–653, 656–665, 668–677, 683–691, 696–729, 735–744, 750–757, 761–767, 771–799, 803–824, 828–862, 866–868, 872–874, 878–887
   featureselector.py645711%43–47, 52–93, 98–124
   metadata.py674828%21–30, 34, 38, 42, 46, 50, 54, 74–115, 135–144, 149, 154
   utils.py1131039%44–110, 127–173, 201, 217–253, 258, 263
bdpy/dataform
   datastore.py1078521%59–75, 90–93, 97–98, 102–113, 116–119, 122–127, 131–132, 137–158, 190–197, 222–259, 262–265
   features.py29823322%29–32, 41–47, 74–103, 107, 111, 115, 119, 137–163, 168–197, 213–238, 241–269, 272–275, 278–289, 305–319, 323, 327, 331, 335, 339, 343, 347, 351, 355, 359, 364–394, 398–418, 422–462, 465, 470–477, 491–493, 496–499, 502–505, 508–512, 515–516, 536–549
   kvs.py1401400%4–272
   pd.py9544%25–27, 43–44
   sparse.py675124%19–29, 35–46, 52–58, 65–74, 78, 81–87, 90–93, 96–98, 101–126
   utils.py12120%3–18
bdpy/dataset
   utils.py45450%3–98
bdpy/distcomp
   distcomp.py927815%20–29, 32–49, 52–70, 73–93, 96–107, 111, 114–117, 121–127
bdpy/dl
   caffe.py60600%4–129
bdpy/dl/torch
   base.py432444%31–41, 48, 54, 60, 63, 73–83, 90, 96, 102, 105
   models.py33329811%28–84, 114–140, 148–169, 175–238, 249–253, 259–279, 288–292, 297–316, 327–331, 340–405, 427–431, 442–494, 515–517, 528–587, 611–614, 625–684, 708–711, 722–771, 790–793, 804–853, 872–875
   torch.py1098423%40–57, 60, 77–95, 100, 107, 110, 115, 122, 125, 172–202, 205, 208–220, 223–258
bdpy/evals
   metrics.py95878%12–34, 40–73, 82–112, 118–159, 172–179
bdpy/feature
   feature.py30287%33–74
bdpy/fig
   __init__.py440%6–9
   draw_group_image_set.py88880%3–182
   fig.py88880%16–164
   makeplots.py3363360%1–729
   tile_images.py59590%1–193
bdpy/ml
   crossvalidation.py59548%34–61, 104–128, 138, 164–196
   ensemble.py13931%33–46
   learning.py30926514%43–44, 48, 52, 59, 91–104, 109–125, 128, 158–170, 184–209, 260–284, 290–433, 436–461, 465–504, 507–508, 524–536, 541–613
   model.py14012014%29–39, 54–70, 86–144, 156–169, 184–222, 225, 230–250, 254–258, 271–285
   regress.py11827%29–38
   searchlight.py161319%32–51
bdpy/mri
   fmriprep.py4974529%25–34, 38, 44–62, 65–75, 78–89, 92–160, 163–194, 230–360, 367–380, 384, 388–390, 394, 398–400, 410–434, 437–454, 457–464, 471–472, 475–491, 494, 498, 502–815, 819–831, 842–862, 866
   glm.py403610%46–95
   image.py241921%29–54
   load_epi.py281836%36–50, 56–63, 82–88
   load_mri.py191616%16–36
   roi.py2482346%37–100, 122–148, 165–235, 241–314, 320–387, 399–466, 473–499
   spm.py15813912%26–155, 162–166, 170, 174–179, 183–300
bdpy/opendata
   __init__.py110%1
   openneuro.py2102100%1–329
bdpy/pipeline
   config.py362919%15–64
bdpy/preproc
   interface.py524415%31–40, 60–69, 96–105, 111–123, 148–157, 208–217
   preprocessor.py12910717%35, 43–65, 74–78, 85–97, 104–132, 138–189, 196–227, 234–239
   select_top.py231822%35–60
   util.py6267%14, 22
bdpy/recon
   utils.py55550%4–146
bdpy/recon/torch
   __init__.py110%1
   icnn.py1611610%15–478
bdpy/stats
   corr.py433812%29–77, 96–112
bdpy/util
   info.py473623%19–79
   math.py131023%23–38
   utils.py362628%48–66, 93–96, 116–121, 137–145
TOTAL5068445412% 

Tests Skipped Failures Errors Time
1 0 💤 0 ❌ 1 🔥 5.680s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
bdpy/bdata
   bdata.py40032020%73–79, 86, 90, 95, 99, 104, 109, 113, 118, 122, 132–134, 155–172, 190, 206–207, 232–248, 252–262, 276–277, 293, 310, 314, 318–356, 388–477, 508, 539, 547–551, 560, 577–584, 589–598, 604–614, 618, 622–625, 628, 632–653, 656–665, 668–677, 683–691, 696–729, 735–744, 750–757, 761–767, 771–799, 803–824, 828–862, 866–868, 872–874, 878–887
   featureselector.py645711%43–47, 52–93, 98–124
   metadata.py674828%21–30, 34, 38, 42, 46, 50, 54, 74–115, 135–144, 149, 154
   utils.py1131039%44–110, 127–173, 201, 217–253, 258, 263
bdpy/dataform
   datastore.py1078521%59–75, 90–93, 97–98, 102–113, 116–119, 122–127, 131–132, 137–158, 190–197, 222–259, 262–265
   features.py29823322%29–32, 41–47, 74–103, 107, 111, 115, 119, 137–163, 168–197, 213–238, 241–269, 272–275, 278–289, 305–319, 323, 327, 331, 335, 339, 343, 347, 351, 355, 359, 364–394, 398–418, 422–462, 465, 470–477, 491–493, 496–499, 502–505, 508–512, 515–516, 536–549
   kvs.py1401400%4–272
   pd.py9544%25–27, 43–44
   sparse.py675124%19–29, 35–46, 52–58, 65–74, 78, 81–87, 90–93, 96–98, 101–126
   utils.py12120%3–18
bdpy/dataset
   utils.py45450%3–98
bdpy/distcomp
   distcomp.py927815%20–29, 32–49, 52–70, 73–93, 96–107, 111, 114–117, 121–127
bdpy/dl
   caffe.py60600%4–129
bdpy/dl/torch
   base.py432444%31–41, 48, 54, 60, 63, 73–83, 90, 96, 102, 105
   models.py33329811%28–84, 114–140, 148–169, 175–238, 249–253, 259–279, 288–292, 297–316, 327–331, 340–405, 427–431, 442–494, 515–517, 528–587, 611–614, 625–684, 708–711, 722–771, 790–793, 804–853, 872–875
   torch.py1098423%40–57, 60, 77–95, 100, 107, 110, 115, 122, 125, 172–202, 205, 208–220, 223–258
bdpy/evals
   metrics.py95878%12–34, 40–73, 82–112, 118–159, 172–179
bdpy/feature
   feature.py30287%33–74
bdpy/fig
   __init__.py440%6–9
   draw_group_image_set.py88880%3–182
   fig.py88880%16–164
   makeplots.py3363360%1–729
   tile_images.py59590%1–193
bdpy/ml
   crossvalidation.py59548%34–61, 104–128, 138, 164–196
   ensemble.py13931%33–46
   learning.py30926514%43–44, 48, 52, 59, 91–104, 109–125, 128, 158–170, 184–209, 260–284, 290–433, 436–461, 465–504, 507–508, 524–536, 541–613
   model.py14012014%29–39, 54–70, 86–144, 156–169, 184–222, 225, 230–250, 254–258, 271–285
   regress.py11827%29–38
   searchlight.py161319%32–51
bdpy/mri
   fmriprep.py4974529%25–34, 38, 44–62, 65–75, 78–89, 92–160, 163–194, 230–360, 367–380, 384, 388–390, 394, 398–400, 410–434, 437–454, 457–464, 471–472, 475–491, 494, 498, 502–815, 819–831, 842–862, 866
   glm.py403610%46–95
   image.py241921%29–54
   load_epi.py281836%36–50, 56–63, 82–88
   load_mri.py191616%16–36
   roi.py2482346%37–100, 122–148, 165–235, 241–314, 320–387, 399–466, 473–499
   spm.py15813912%26–155, 162–166, 170, 174–179, 183–300
bdpy/opendata
   __init__.py110%1
   openneuro.py2102100%1–329
bdpy/pipeline
   config.py362919%15–64
bdpy/preproc
   interface.py524415%31–40, 60–69, 96–105, 111–123, 148–157, 208–217
   preprocessor.py12910717%35, 43–65, 74–78, 85–97, 104–132, 138–189, 196–227, 234–239
   select_top.py231822%35–60
   util.py6267%14, 22
bdpy/recon
   utils.py55550%4–146
bdpy/recon/torch
   __init__.py110%1
   icnn.py1611610%15–478
bdpy/stats
   corr.py433812%29–77, 96–112
bdpy/util
   info.py473623%19–79
   math.py131023%23–38
   utils.py362628%48–66, 93–96, 116–121, 137–145
TOTAL5068445412% 

Tests Skipped Failures Errors Time
1 0 💤 0 ❌ 1 🔥 4.876s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
bdpy/bdata
   bdata.py39931920%73–79, 86, 90, 95, 99, 104, 109, 113, 118, 122, 132–134, 155–172, 190, 206–207, 232–248, 252–262, 276–277, 293, 310, 314, 318–356, 388–477, 508, 539, 547–551, 560, 577–584, 589–598, 604–614, 618, 622–625, 628, 632–653, 656–665, 668–677, 683–691, 696–729, 735–744, 750–757, 761–767, 771–799, 803–824, 828–862, 866–868, 872–874, 878–887
   featureselector.py645711%43–47, 52–93, 98–124
   metadata.py674828%21–30, 34, 38, 42, 46, 50, 54, 74–115, 135–144, 149, 154
   utils.py1131039%44–110, 127–173, 201, 217–253, 258, 263
bdpy/dataform
   datastore.py1078521%59–75, 90–93, 97–98, 102–113, 116–119, 122–127, 131–132, 137–158, 190–197, 222–259, 262–265
   features.py29823322%29–32, 41–47, 74–103, 107, 111, 115, 119, 137–163, 168–197, 213–238, 241–269, 272–275, 278–289, 305–319, 323, 327, 331, 335, 339, 343, 347, 351, 355, 359, 364–394, 398–418, 422–462, 465, 470–477, 491–493, 496–499, 502–505, 508–512, 515–516, 536–549
   kvs.py1401400%4–272
   pd.py9544%25–27, 43–44
   sparse.py675124%19–29, 35–46, 52–58, 65–74, 78, 81–87, 90–93, 96–98, 101–126
   utils.py12120%3–18
bdpy/dataset
   utils.py45450%3–98
bdpy/distcomp
   distcomp.py927815%20–29, 32–49, 52–70, 73–93, 96–107, 111, 114–117, 121–127
bdpy/dl
   caffe.py60600%4–129
bdpy/dl/torch
   base.py432444%31–41, 48, 54, 60, 63, 73–83, 90, 96, 102, 105
   models.py33329811%28–84, 114–140, 148–169, 175–238, 249–253, 259–279, 288–292, 297–316, 327–331, 340–405, 427–431, 442–494, 515–517, 528–587, 611–614, 625–684, 708–711, 722–771, 790–793, 804–853, 872–875
   torch.py1098423%40–57, 60, 77–95, 100, 107, 110, 115, 122, 125, 172–202, 205, 208–220, 223–258
bdpy/evals
   metrics.py95878%12–34, 40–73, 82–112, 118–159, 172–179
bdpy/feature
   feature.py30287%33–74
bdpy/fig
   __init__.py440%6–9
   draw_group_image_set.py90900%3–182
   fig.py88880%16–164
   makeplots.py3363360%1–729
   tile_images.py59590%1–193
bdpy/ml
   crossvalidation.py59548%34–61, 104–128, 138, 164–196
   ensemble.py13931%33–46
   learning.py30826414%43–44, 48, 52, 59, 91–104, 109–125, 128, 158–170, 184–209, 260–284, 290–433, 436–461, 465–504, 507–508, 524–536, 541–613
   model.py14012014%29–39, 54–70, 86–144, 156–169, 184–222, 225, 230–250, 254–258, 271–285
   regress.py11827%29–38
   searchlight.py161319%32–51
bdpy/mri
   fmriprep.py4974519%25–34, 38, 44–62, 65–75, 78–89, 92–160, 163–194, 230–360, 367–380, 384, 388–390, 394, 398–400, 410–434, 437–454, 457–464, 471–472, 475–491, 494, 498, 502–815, 819–831, 842–862
   glm.py403610%46–95
   image.py241921%29–54
   load_epi.py281836%36–50, 56–63, 82–88
   load_mri.py191616%16–36
   roi.py2482346%37–100, 122–148, 165–235, 241–314, 320–387, 399–466, 473–499
   spm.py15813912%26–155, 162–166, 170, 174–179, 183–300
bdpy/opendata
   __init__.py110%1
   openneuro.py2102100%1–329
bdpy/pipeline
   config.py362919%15–64
bdpy/preproc
   interface.py524415%31–40, 60–69, 96–105, 111–123, 148–157, 208–217
   preprocessor.py12910717%35, 43–65, 74–78, 85–97, 104–132, 138–189, 196–227, 234–239
   select_top.py231822%35–60
   util.py6267%14, 22
bdpy/recon
   utils.py55550%4–146
bdpy/recon/torch
   __init__.py110%1
   icnn.py1611610%15–478
bdpy/stats
   corr.py433812%29–77, 96–112
bdpy/util
   info.py473623%19–79
   math.py131023%23–38
   utils.py362628%48–66, 93–96, 116–121, 137–145
TOTAL5068445312% 

Tests Skipped Failures Errors Time
1 0 💤 0 ❌ 1 🔥 4.173s ⏱️

Please sign in to comment.